Обзор
Правило Travel Rule FATF (Рекомендация 16), реализованное в ЕС в виде Регламента о переводах средств (TFR), требует, чтобы отправляющий поставщик услуг виртуальных активов (VASP-отправитель) собрал и передал информацию об отправителе и получателе VASP-получателю (VASP-получатель) до того, как перевод виртуальных активов будет завершён. В отличие от большинства юрисдикций, TFR ЕС действует без минимальной суммы: в сферу действия попадает каждый криптоперевод, а не только переводы выше порога отчётности. Продукт Didit для Travel Rule представляет собой управляемый обмен, а не просто поле с данными:- Вы отправляете перевод с
transaction_category: "travelRule". - Didit перебирает каналы в порядке приоритета, чтобы связаться с поставщиком-контрагентом, проводит обмен через первый канал, по которому удаётся связаться с ним, и, если другая сторона возвращает имя получателя, сверяет его с тем, которое отправили вы.
- Только после того, как обмен перейдёт в статус
COMPLETED, следует транслировать перевод ончейн. - Затем вы передаёте в Didit хеш блокчейн-транзакции, чтобы закрыть обмен со статусом
FINISHED.
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, а что каждая сеть охватывает сегодня, см. Travel Rule networks.
Если все четыре сообщают 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.
Остальные сети: CODE (корейский альянс), TRISA, Notabene и Veriscope перечислены в Marketplace в консоли в виде плиток для запроса доступа.
Интеграции с ними у Didit пока нет, поэтому сегодня через них нельзя направить ни один перевод.
Если ни один из семи каналов не может определить контрагента, Didit выполняет еще один шаг определения, прежде чем сдаться: атрибуцию через блокчейн-аналитику.
Адрес кошелька назначения сверяется с данными блокчейн-аналитики; если кошелек принадлежит известной бирже или кастодиану, Didit записывает атрибутированный VASP в перевод, добавляет его в справочник и один раз повторно запускает каналы с новым названием.
Подробнее о поведении и ценах см. Атрибуция через блокчейн-аналитику.
Перевод, адрес назначения которого объявлен как некастодиальный кошелек, пропускает все семь каналов, так как VASP-контрагента для обмена нет, и сразу переходит к подтверждению владения кошельком конечным пользователем.
/v3/, описанные на этой странице.Что выполняется при отправке перевода
Проверки выполняются в рамках одного вызоваPOST /v3/transactions/. К моменту получения ответа скрининг уже выполнен, перевод сохранён, а правила оценены.
Обмен начинается уже после ответа: Didit выбирает канал и связывается с контрагентом вне вашего запроса, поэтому ни одна сеть и ни один контрагент не могут замедлить создание или привести к его ошибке.
Каждое последующее изменение статуса приходит в виде webhook travel_rule.status.updated, в котором указан канал. Объект перевода, который возвращают endpoint’ы Travel Rule, содержит поле delivery, показывающее, дошло ли сообщение.
Опрашивать ничего не нужно. Асинхронными остаются обмен, ответ контрагента и действия конечного пользователя.
Пошагово:
- Валидация и привязка субъекта. Payload проверяется.
Если
transaction_idуже использовался в вашем приложении, запрос отклоняется с кодом400. Если два запроса с одинаковым идентификатором приходят одновременно, один из них получает409.subjectсвязывается с вашим профилем пользователя или компании черезvendor_data, чтобы перевод отображался в этом профиле для мониторинга. Еслиvendor_dataсубъекта или контрагента находится в списке блокировки, запрос отклоняется с кодом400, и транзакция не создаётся. Кошельки, банковские счета, IP-адреса и устройства из списка блокировки проверяются на шаге 6, где транзакция отклоняется уже после маршрутизации. - Обогащение данными об устройстве и сети. IP-адреса участников обогащаются геоданными. Контекст устройства (он передаётся автоматически при отправке через SDK) нормализуется и попадает в граф устройств. Это тот же граф, который наполняют сессии KYC, поэтому связь «по устройству» работает и при онбординге, и для транзакций.
- Криптоскрининг. Для криптопереводов с включённым мониторингом соответствующий кошелёк (или ончейн-хеш) проверяется в аналитике блокчейна до того, как начнётся какой-либо обмен: риск санкций, близость к миксерам, украденные средства. См. Криптоскрининг и скрининг кошельков.
- Перевод Travel Rule. Движок обмена проверяет порог и учитывает декларацию о некастодиальном кошельке, после чего сохраняет перевод. Статус будет
NOT_APPLICABLE, если сумма ниже вашего порога;UNCONFIRMED_OWNERSHIPдля заявленного некастодиального кошелька;COMPLETED, если второй кошелёк принадлежит вам и проверен. Во всех остальных случаяхAWAITING_COUNTERPARTY, а канал на этом этапе ещё не выбран. Каждое изменение статуса ставит в очередь событиеtravel_rule.status.updated. Didit отправляет его послеtransaction.created(шаг 7). - Выпуск сессии подтверждения владения кошельком. Если переводу нужно подтверждение контроля над кошельком со стороны конечного пользователя и включён параметр
auto_wallet_verification, создаётся сессия виджета подтверждения владения кошельком, которая возвращается вaction_required. - Оценка правил. Выполняются все активные правила. Пакет правил EU TFR реагирует на статус перевода, заданный на шаге 4. Ваши пользовательские правила видят результаты скрининга шага 3. Правило может удержать транзакцию (
IN_REVIEW), отклонить её или потребовать дополнительной верификации (AWAITING_USER). - Биллинг и уведомления. За исходящую транзакцию взимается плата по одной строке: линия Travel Rule, если она запустила обмен, и скрининг транзакций в остальных случаях. С входящего перевода плата не взимается, а за приложение в песочнице плата никогда не взимается.
Ответ возвращается, после чего
transaction.createdотправляется на ваши адреса назначения webhook, а затем следуют событияtravel_rule.status.updatedшага 4. - Маршрутизация, после ответа. 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транзакции с помощью Получить транзакцию.
Как каждый этап соответствует вашим обязательствам
transaction.created или transaction.status.updated. Транзакции в песочнице остаются в статусе APPROVED, если только при истечении перевода Travel Rule не применяется ваш timeout_outcome. Оплата в песочнице не взимается.
Движок обмена Travel Rule работает в приложениях песочницы, если Travel Rule включён, и взаимодействует только с одним контрагентом. Каждый исходящий обмен направляется к Didit Test VASP по каналу TEST_VASP. Тестовый VASP отвечает внутри Didit по выбранному вами сценарию, поэтому перевод может завершиться, быть отклонён, завершиться несоответствием данных, стать недоступным, истечь или перейти в ожидание подтверждения владения кошельком.
Сообщения, связанные с комплаенсом, никогда не доходят до реального VASP-контрагента, а webhook travel_rule.status.updated отправляется с environment: sandbox.
Для входящих переводов: тестовый VASP может отправить вам перевод (Инициировать входящий перевод от тестового VASP). Регистрация (POST /v3/travel-rule/inbound/) сверяется с адресной книгой кошельков так же, как в боевом приложении: ON_HOLD, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_MISMATCHED_DATA или COMPLETED, а затем FINISHED.
См. Тестирование Travel Rule в песочнице.Lifecycle
Каждый перевод имеетstatus из 13 значений.
Обмен начинается со статуса AWAITING_COUNTERPARTY в обычном случае, со статуса NOT_APPLICABLE, если сумма ниже вашего threshold_amount или в запросе на платёж Lightning не указан получатель (см. Lightning), либо со статуса UNCONFIRMED_OWNERSHIP, если адрес назначения объявлен некастодиальным кошельком. Завершается он одним из конечных статусов: FINISHED, CANCELLED или EXPIRED.
У перевода собственный конечный автомат, отдельный от статуса проверки базовой транзакции.
Статус обмена сам по себе никогда не меняет статус транзакции, за одним исключением: при истечении срока применяется ваш настроенный timeout_outcome (или политика sunrise или для неотвечающих контрагентов, если она действует).
Вместо этого ваши правила пересчитываются при каждом переходе, поэтому правила EU TFR или любое правило, которое опирается на travel_rule_status, может изменить транзакцию, например перевести её в IN_REVIEW.
Если переводу нужно подтверждение владения кошельком от конечного пользователя, в транзакцию добавляется блок action_required, но её status остаётся таким, каким его оставили ваши правила.
Состояние обмена читайте из travel_rule.status, а решение «разрешить / удержать / отклонить» из собственного status транзакции.
Каждый переход (и каждый статус, полученный пассивно) также дублируется в пользовательские значения транзакции, чтобы на них могли опираться правила: 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, которое вы передаёте сами, всегда имеет приоритет над вычисленным.
CANCELLED дополнительно достижим из каждого нетерминального статуса с action: "cancel"):
Доставка и повторные попытки
Didit отправляет каждое сообщение, которое перевод должен отправить, вне запроса, который его вызвал, и повторяет отправку, пока другая сторона не примет его. Объект перевода, возвращаемый в ответе на Завершение, отмена или повторная отправка, Подтверждение владения и регистрация sunrise, содержит последнее такое сообщение в полеdelivery, то же относится и к встроенному объекту travel_rule транзакции; оно равно null, если перевод никогда не требовал такого сообщения.
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) также принимается, как при отправке, так и в значениях условий правил, и приводится к канонической форме:
Настройка
Настройте профиль VASP и политику согласования один раз для каждого приложения с помощьюGET/PUT /v3/travel-rule/settings/.
PUT выполняет частичное обновление: передавайте только те поля, которые хотите изменить.
Строгость сопоставления имён
Режимы проверки владения
ownership_validation_mode определяет, какое подтверждение владения кошельком требуется, прежде чем перевод сможет использовать этот кошелёк.
FULL и SIMPLIFIED последующий перевод использует его, только если такое подтверждение, допустимое в этом режиме для данного перевода, подтверждает владение кошельком.
Если для inbound_auto_accept в адресной книге нет подходящего кошелька, владельца определяет криптокошелёк, который вы записали ровно в транзакциях одного конечного пользователя.
Didit добавляет этот кошелёк в адресную книгу конечного пользователя как вашу самодекларацию, с пометкой «верифицирован», если режим не FULL. Didit никогда не добавляет кошелёк, который адресная книга уже содержит под другим именем той же сети (eth и ethereum), включая кошелёк, записанный как принадлежащий другому VASP.
Проверки адресов, которые другие VASP выполняют перед отправкой (через GTR, Sygna и VerifyVASP), также следуют режиму: при FULL кошелёк, который конечный пользователь только задекларировал, не сообщается как принадлежащий вам.
Передача данных
data_sharing_fields определяет, какие поля Didit передаёт контрагенту о ваших пользователях, отдельно для физических лиц и компаний:
- Физические лица могут передавать
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 задаёт, насколько строго сравнивается каждое поле данных контрагента с тем, что у вас есть:
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, позицию ЕС, поэтому по каждому переводу происходит обмен данными, пока вы сознательно не повысите порог:
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 внутри транзакции, фиксируется, откуда взято каждое поле и где ваши значения отличались:
*_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 имеет приоритет над блоком для той же стороны.
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.".
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.
Если вам известен 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 символов:
400 с "Invalid travel address.".
Берите настоящий адрес из ответа с вашими настройками или от самого контрагента.
Тестирование без сетевых учётных данных
Для тестирования сценариев, не требующих членства, не нужно состоять в GTR, TRUST, VerifyVASP или Sygna. Но для тестируемого приложения по-прежнему нужен API-ключ Didit.- Исходящие в песочнице: отправьте транзакцию
travelRuleиз приложения песочницы. После проверок порога и некастодиального кошелька Didit Test VASP отвечает на неё внутри Didit по сценарию, который вы выберете по кошельку или имени получателя: принятие, отклонение, несовпадение имени, недоступен, таймаут или некастодиальный. Ни сеть, ни электронная почта, ни другой клиент Didit ничего не получают. - Входящие в песочнице: попросите тестовый VASP отправить вам перевод с помощью Trigger Test VASP Inbound Transfer или зарегистрируйте его сами через Register Inbound Travel Rule Transfer. Проверьте неизвестный кошелёк, непроверенный кошелёк, несовпадение имени получателя и проверенный совпадающий кошелёк. Эти сценарии воспроизводят реальные решения по входящим переводам без обращения к сети.
- Два контролируемых продакшн-приложения: используйте канал
INTERNALмежду двумя вашими продакшн-приложениями (принимающее приложение доступно для поиска и подтверждает контроль над кошельком назначения подписанным сообщением или тестовым депозитом) либо контролируемого контрагента TRP, чтобы протестировать обмен между двумя сторонами без коммерческих сетевых учётных данных. Продакшн-приложения могут приводить к обычным списаниям и уведомлениям, поэтому используйте тестовые данные о личности и контрагентов, которых вы контролируете.
Отправка перевода
Включите перевод вPOST /v3/transactions/, установив transaction_category в значение travelRule (или travel_rule) и прикрепив объект travel_rule_details (псевдоним: travelRule).
Блок travel_rule_details необязателен, но если вы его прикрепили, поле travel_rule_details.status обязательно: блок без него возвращает 400.
Если Travel Rule включено для вашего приложения, Didit игнорирует переданное вами значение status и сам управляет обменом:
- Если сумма ниже настроенного вами
threshold_amount, перевод сразу переходит вNOT_APPLICABLE, и никакой обмен не выполняется. - Если платёжный метод контрагента указывает, что адрес назначения принадлежит некастодиальному кошельку (
payment_method.method_type: "unhosted_wallet"), каналы не пробуются: перевод сразу переходит вUNCONFIRMED_OWNERSHIP, и виджет подтверждения владения кошельком создаётся автоматически (см. Объявленные некастодиальные кошельки). - В остальных случаях каналы запускаются в порядке приоритета, и первый канал, который может связаться с контрагентом, ведёт обмен:
INTERNAL– ровно один другой клиент Didit имеет подписанное сообщение или тестовый депозит как доказательство владенияbeneficiary_data.wallet_address, созданное после последней смены владельца кошелька. Ваше собственное приложение никогда не учитывается, а если ваш конечный пользователь выбрал участника сети Didit в виджете подтверждения владения кошельком, учитывается только доказательство этого участника. Приложение этого клиента работает в продакшн-среде (не в песочнице), имеет включённое Travel Rule и установленныйis_discoverable, а также является тем VASP, которого вы указали при передачеcounterparty_vasp_leiилиcounterparty_vasp_name. Кошелёк, который только заявлен самостоятельно, подтверждён скриншотом, не верифицирован или лишь указан в адресной книге, никогда не получает ничего черезINTERNAL.TRP– для актива перевода в сети перевода есть сопоставление Digital Token Identifier (см. Идентификаторы активов 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соответствует записи в справочнике с контактной электронной почтой для комплаенса.
- Если ни один канал не может определить контрагента, Didit запускает цепочку поиска контрагента. Если включён
vasp_attribution_enabled, цепочка включает атрибуцию через блокчейн-аналитику: адрес назначения кошелька сопоставляется с VASP, которому принадлежит кошелёк, атрибутированное имя записывается вbeneficiary_data.counterparty_vasp_name(если поле пустое) и в справочник, после чего каналы запускаются повторно один раз. Поэтому кошелёк, атрибутированный VASP, у записи которого в справочнике есть endpoint TRP, идентификатор сети или контактная электронная почта для комплаенса, маршрутизируется уже на этом втором проходе. - Если контрагент всё ещё не определён, перевод переходит в
COUNTERPARTY_VASP_NOT_FOUND. Для исходящих переводов Didit автоматически создаёт виджет подтверждения владения кошельком (когда включёнauto_wallet_verification), чтобы перевод всё же мог завершиться как перевод на некастодиальный кошелёк. Позже, когда будут более точные данные о контрагенте, вы можете повторно запустить маршрутизацию сaction: "resend".
AWAITING_COUNTERPARTY и protocol: null, а каждый результат приходит в виде webhook travel_rule.status.updated.
Идентификаторы активов TRP
TRP обозначает передаваемый актив с помощью его ISO 24165 Digital Token Identifier (DTI).
DTI идентифицирует токен в одном блокчейне, поэтому у токена, существующего в нескольких сетях, есть отдельный DTI для каждой сети.
Didit отправляет переводы через TRP для следующих активов:
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 с маршрутом: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:
url для действий, которые выполняет конечный пользователь.
Когда ожидается действие по подтверждению владения кошельком, блок action_required транзакции (тип wallet_ownership) содержит то же reason и next_actions, поэтому клиент, который уже следует action_required, покажет выбор без дополнительной работы.
Поля определения контрагента
Всё, что Didit известно о контрагенте, берётся изtravel_rule_details.beneficiary_data.
Эти поля управляют определением канала; все они необязательны:
Типы цепочек
В цепочке на основе аккаунтов (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).
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 или Lightningpayment_method.account_id может быть этим запросом, и Didit определяет получателя по нему, не обращаясь ни к одной сети:
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). В любой другой сети это собственный адрес этой сети.
INTERNAL достигает другого клиента Didit только через кошелёк, контроль над которым клиент подтвердил подписанным сообщением или тестовым депозитом. Такие подтверждения сегодня существуют для Bitcoin, EVM-сетей, Solana и Tron.
Поэтому в сетях с memo, Lightning, Litecoin, Bitcoin Cash и Dogecoin перевод никогда не достигает другого клиента Didit через INTERNAL.
Сопоставление по memo, тегам и Lightning по-прежнему применяется к вашей собственной адресной книге: входящие переводы, сообщённые депозиты и запросы TRP к вашим кошелькам.Обновление кошелька отправителя
Вывод средств часто объявляется с основного кошелька вашего клиента, а транслируется с тех адресов, на которых находились монеты. Запишите кошелёк, с которого средства фактически ушли, черезPATCH /v3/transactions/{transaction_id}/travel-rule/:
- Только для исходящих переводов, и никогда для переводов со статусом
CANCELLEDилиEXPIRED(409). - Если входы UTXO перевода известны, кошелёк должен быть одним из них (иначе
400). В сети с memo отправьте такжеmemo. - Изменяется только запись: контрагенту ничего не отправляется повторно, статус сохраняется.
chain_details.originatorстановится новым кошельком сsource: updated, вtimelineфиксируются предыдущий и новый кошелёк, а встречная записьINTERNALперевода обновляется так же.
Атрибуция в блокчейн-аналитике
Если вы передаёте только адрес кошелька, Didit всё равно может определить, кто находится на другой стороне. Если ни один канал не может маршрутизировать перевод, Didit сопоставляет кошелёк назначения с данными атрибуции из блокчейн-аналитики: тот же анализ, на котором работает скрининг криптотранзакций, знает, какие кошельки принадлежат каким биржам и кастодианам. Атрибуция выполняется только для исходящих переводов, которые содержат и кошелёк назначения, иchain (см. Поля определения контрагента). Без указания сети она пропускается.
Если кошелёк атрибутирован известной сущности, Didit:
- Фиксирует атрибуцию в переводе:
beneficiary_data.counterparty_vasp_nameзаполняется (если вы не передали его сами), а метаданные перевода содержат блокvasp_attributionс названием сущности и типом сущности, страной иsource: "blockchain_analytics". - Добавляет организацию в справочник VASP (или сопоставляет с существующей записью), чтобы атрибуция оставалась стабильной между переводами.
- Повторно запускает каналы один раз с атрибутированным названием. Запись справочника, уже содержащая 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, поскольку в нём указан адрес электронной почты комплаенса контрагента, а ни один из предыдущих каналов не может связаться с получателем:
originator_dataсодержит то, что вы отправили, потому что у субъекта в примере нет одобренной верификации KYC; для проверенного пользователя Didit формирует его из проверенных данных (см. Проверенные данные и их происхождение). КаналEMAILпоказывает получателю только те поля отправителя, которыми вы делитесь вdata_sharing_fields.beneficiary_data.chainравенETH, так как в запросе сеть не была указана и Didit определила её по валюте.statusравенAPPROVED, так как ни одно правило не повлияло на транзакцию; ваши собственные правила могут установить другой статус.- Контакт комплаенса получает ссылку для получения, и перевод завершается, не проходит проверку совпадения имени или отклоняется, когда он отвечает, либо истекает, если он не отвечает (см. Получение через email).
protocol остаётся null, потому что канал выбирается после ответа (например, EMAIL, если вы передали counterparty_vasp_email): Получить транзакцию показывает его после выбора, а каждое последующее событие 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 в транзакции.
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/ содержит такой же блок или null для транзакции без перевода.
Отдельные эндпоинты переводов, описанные ниже, тоже возвращают эти поля, а также ivms_version, ownership_confirmed, deadline_at, timeline и created_at, но без obligations_count и metadata.COMPLETED после того, как вы отправили его в блокчейн:
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.
{"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}/, в ответе 201 на POST /v3/transactions/ и в каждой строке GET /v3/transactions/ содержит его, так же как и объект перевода, который возвращает эндпоинт переводов:
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 очищается.
Запись в таймлайне о переходе, который вызвала неудачная доставка, содержит то же значение в поле reason.
Если почтовый провайдер отклонит комплаенс-письмо для примера перевода выше, блок travel_rule в его транзакции выглядит так (в сокращении):
delivery согласуется с reason, потому что отправка сама по себе была отклонена.
Отказ при напоминании или сообщение о постоянной недоставке, полученное позже, оставляет delivery.status в значении DELIVERED: первое письмо действительно ушло.
Чтобы понять, почему перевод застрял, смотрите reason, а не delivery.
Чтобы исправить ситуацию, передайте адрес электронной почты контрагента для комплаенса повторно, а затем выполните повторную отправку:
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.
- Ни один канал не используется: контрагента-VASP, с которым можно обменяться данными, нет, и перевод сразу создаётся в статусе
UNCONFIRMED_OWNERSHIP. - Если
auto_wallet_verificationвключён (по умолчанию), Didit автоматически создаёт сессию виджета подтверждения владения кошельком, привязанную к транзакции, и возвращает её в блокеaction_requiredтранзакции; SDK запускают её автоматически при отправке на стороне клиента. - Как только клиент подтверждает владение кошельком, перевод выполняет сверку имени получателя и переходит в
COMPLETED(или вCOUNTERPARTY_MISMATCHED_DATA, если сверка не прошла).
UNCONFIRMED_OWNERSHIP не является состоянием, для которого допустим повторный запуск: завершите подтверждение владения (или отмените перевод) вместо вызова action: "resend".
Получение
Подтверждение владения кошельком
Перевод ожидает в статусеUNCONFIRMED_OWNERSHIP в двух случаях:
- Входящий перевод сопоставляется с записью в адресной книге, владение которой ещё не подтверждено.
- Кошелёк контрагента заявлен как некастодиальный (
counterparty.payment_method.method_type: "unhosted_wallet") при исходящем выводе средств или входящем зачислении. См. Заявленные некастодиальные кошельки.
confirmed (обязательный, логического типа) и необязательный wallet_address.
Endpoint принимает перевод в любом из следующих статусов и возвращает 409 с {"detail": "Transfer is not awaiting ownership confirmation."} для любого другого статуса:
UNCONFIRMED_OWNERSHIP, в любом направлении.ON_HOLDдля входящего перевода.UNCONFIRMED_OWNERSHIP,ON_HOLDилиCOUNTERPARTY_MISMATCHED_DATAдля входящего переводаTRP, чтобы комплаенс-офицер мог вручную обработать запрос контрагента.
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, а перевод сохраняет текущий статус. При одобрении запись в адресной книге кошельков всё равно создаётся или помечается как подтверждённая.
COUNTERPARTY_MISMATCHED_DATA.
Если в переводе не указан кошелёк контрагента, передайте wallet_address в теле запроса.
Если ни перевод, ни запрос не содержат адрес кошелька, запрос отклоняется с кодом 400.
- Сетевой перевод, на который уже ответили через его сеть (перевод VerifyVASP или перевод GTR либо Sygna Bridge, по которому уже принято решение), возвращает
409(см. Переводы из сети).
INTERNAL здесь никогда не ожидает: он доступен только клиенту, который уже подтвердил владение кошельком.
Старый перевод INTERNAL, который всё ещё ждёт с тех времён, когда этого правила не было, скрывает данные отправителя, пока вы не докажете контроль над кошельком (подписанным сообщением или тестовым зачислением). Подтверждение такого перевода возвращает 409, пока вы этого не сделаете. Отклонить его по-прежнему можно.
Если контрагент сообщил, что кошелёк некастодиальный (сейчас в песочнице такой ответ даёт Didit Test VASP), подтверждение добавляет кошелёк в вашу адресную книгу на имя получателя и запускает сверку имени: при совпадении перевод завершается, а кошелёк помечается как подтверждённый.
Переводы из сети
Didit также может принимать переводы Travel Rule, которые контрагент отправляет по сетямGTR, VERIFYVASP или SYGNA.
Didit отдельно включает приём для каждой сети, и сейчас он не включён ни для одной: для этого также нужна регистрация endpoint’ов Didit в каждой сети, а членства Didit в этих сетях пока нет.
Переводы TRUST принять нельзя.
Когда сеть включена, сообщение, которое она доставляет, обрабатывается так:
- Маршрутизация. Сообщение попадает в ваше приложение, если в нём указан код участника вашего подключённого членства в этой сети. Сообщение, адресованное членству самого Didit, попадает к вам только в том случае, если вы являетесь единственным действующим клиентом Didit, подтвердившим контроль над кошельком получателя. Всё остальное отклоняется, поэтому ни один клиент никогда не получает перевод другого клиента.
- Решение. Didit создаёт входящий перевод в вашем приложении и принимает по нему решение так же, как по запросу TRP: сопоставление кошелька, владение, сверка имени и
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:
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 перечисляет поля.
Оба параметра содержат только названия полей и результаты, но никогда значения, поэтому остаются читаемыми при маскировании данных:
TRP сообщает VASP-отправителю, какие поля различаются, например: «Данные получателя не совпадают с владельцем кошелька: имя».
Объём информации, которую получает другая VASP, сознательно ограничен: пока имя не совпадает, сообщается только это поле (mismatched_fields: ["name"], а все остальные поля имеют значение not_compared). Когда имя совпадает, называются все поля, которые различаются. Это относится к стороне отправителя при обмене INTERNAL и к отказу по TRP.
Имя, отсутствующее с обеих сторон, получает значение not_compared и не считается расхождением. Страна сравнивается по тому, что обозначает, поэтому ES и ESP совпадают.
Оценка совпадения имён
Каждый перевод, в котором сравнивалось имя, содержитname_match, а запись хронологии, которая определила результат, содержит тот же объект рядом со своим detail:
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/ выводят список сообщений по переводу и отправляют новые:
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}/.
PATCH принимает holder_name, holder_vendor_data, entity_type и travel_address и возвращает только эти четыре поля.
DELETE удаляет запись (204): её адрес освобождается для повторной регистрации, и она сразу перестаёт сопоставляться с новыми переводами.
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/ в формате JSON ({"entries": [...]} или простой массив) или как файл CSV в кодировке UTF-8 размером не более 5 МБ со строкой заголовков.
Строка задаёт кошелёк через address, через address_hash (SHA-256 адреса в нижнем регистре, 64 шестнадцатеричных или 44 символа base64) либо через оба поля, а также любые поля записи, перечисленные выше.
Чтобы зарегистрировать кошелёк, не раскрывая его адрес, передайте только хеш: такая запись сопоставляется с переводами на этот адрес, как любая другая, и её владение можно подтвердить через виджет подтверждения владения кошельком.
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 записи реестра оставляет запрос открытым для напоминаний.
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 знаков после запятой, а не сокращённый вид, который используется в объекте деталей транзакции.
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:
GET /v3/travel-rule/transfers/, но только входящие депозиты, которые ждут данных от отправляющего VASP.
Когда данные пришли отдельным входящим сообщением, сопоставьте их самостоятельно:
{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/.
originating_vasp.travel_address должен быть настоящим Travel Address с верной контрольной суммой Travel Address. Заглушка вернёт 400 с сообщением "Invalid travel address.".
originating_vasp.lei должен быть пустым или состоять из 20 символов в формате LEI (18 букв или цифр, затем 2 цифры). Любое другое значение вернёт 400 с сообщением "Invalid LEI: expected 20 alphanumeric characters (ISO 17442).".
Didit проверяет только формат, а не контрольные цифры ISO 17442.
travelRule (или повторно использует входящий депозит, который вы уже отправили с тем же хешем и кошельком, если у него ещё нет перевода по Travel Rule) и сопоставляет перевод с вашей адресной книгой кошельков по адресу и memo:
- Совпадающей записи нет ->
ON_HOLD. Разрешите ситуацию черезPOST .../travel-rule/ownership/:confirmed: trueдобавляет кошелёк в адресную книгу как верифицированный и запускает сверку имени. При совпадении перевод переходит вCOMPLETED, а затем вFINISHED. - Запись есть, но владение не подтверждено ->
UNCONFIRMED_OWNERSHIP. - Имя получателя не совпадает с полем
holder_nameзаписи ->COUNTERPARTY_MISMATCHED_DATA. - Запись верифицирована и имя совпадает ->
COMPLETED, затем сразуFINISHED(депозит уже зачислен).
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, а затем эти события.
Регистрация не взимает плату за 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/.
См. Виджет подтверждения владения кошельком: там описаны полный процесс, методы подтверждения и важное требование. Виджет нужно открывать в настоящем браузере, но никогда внутри webview мобильного SDK.
Каталог
GET /v3/travel-rule/vasps/ ищет по каталогу VASP: общие записи каталога плюс участники сети Didit. Каждая строка показывает, как ваше приложение может связаться с этим VASP сегодня.
search ищет совпадение с фрагментом названия или псевдонима, точным LEI или доменом (counterparty.example или URL на нём). Сузить результаты можно с помощью параметров:
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, поэтому для него недоступен ни один маршрут из каталога: каждый маршрут имеет 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 контактов контрагентов:
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, ваш скрининг контрагента на санкции и негативные публикации в СМИ, а также анкеты должной осмотрительности, которыми вы обменялись с ним, принадлежат только вашему приложению. Другие клиенты их не видят, и они не меняют общую запись.
id, который показывает справочник.
Оценка и уровень риска
dd_score принимает значения от 0 до 100, и чем выше, тем безопаснее. Каждый результат поясняет это в dd_score_semantic, которое всегда равно higher_is_safer. risk_level определяется диапазоном, в который попадает оценка:
dd_score_breakdown показывает каждый из них в виде {points, max, reason, detail}: max задаёт вес компонента, reason является машиночитаемым кодом, который можно перевести, а detail содержит пояснение на английском в одну строку.
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 выполняет для каждой записи справочника, и только по проверенным источникам платформы, а никогда по данным, переданным клиентом:
checks сообщает свой status (pass, fail или unknown, если ни один источник в записи не позволяет определить её результат), машиночитаемый reason и detail на английском в одну строку. passed, failed, unknown и total подсчитывают проверки, level равен essential, а checked_at показывает, когда был сформирован отчёт. Компонент essentials оценивает долю пройденных проверок среди проверок с определённым результатом, поэтому проверка со статусом unknown не учитывается ни в пользу записи, ни против неё. Отчёт пуст, пока еженедельное обновление справочника впервые не проверит запись, и всегда пуст для участника сети Didit.
Отзывчивость
responsiveness показывает, отвечает ли VASP на запросы Travel Rule:
total подсчитывает запросы, которые клиенты Didit направили VASP за последние 90 дней и результат которых известен, а answered те из них, на которые VASP ответил:
- Учитываются: запросы, отправленные по сетевому каналу или каналу протокола, и запросы, отправленные по электронной почте на общий адрес, то есть на адрес комплаенса записи или на контакт платформы.
- Никогда не учитываются: запрос, отправленный на адрес электронной почты, который ввёл один клиент, или на личный контакт клиента. Такой обмен остаётся в истории переводов этого клиента и никогда не присваивает VASP метку для всех остальных.
- Ответ получен:
COMPLETED,FINISHED,COUNTERPARTY_VASP_GENERAL_DECLINEилиCOUNTERPARTY_MISMATCHED_DATA. Отказ или несоответствие данных тоже считаются ответом: VASP вступил во взаимодействие. - Ответа нет:
EXPIRED. - Ни то ни другое: любой другой статус. Запрос со статусом
CANCELLEDявляется действием самого отправителя,COUNTERPARTY_VASP_NOT_REACHABLEможет быть сбоем на стороне отправителя, а запрос, который ещё выполняется, пока не имеет результата.
UNKNOWN с нулевыми счётчиками. Для неотвечающего контрагента действуйте по политике неответов или по пресету travel-rule-counterparty-unresponsive.
Ваше решение по контрагенту
Зафиксируйте вердикт вашей команды комплаенса по контрагенту с помощьюPUT /v3/travel-rule/vasps/{vasp_id}/decision/:
- Решение видно только в вашем приложении: другой клиент его не видит, и оно никогда не меняет общую запись.
- Само по себе решение ничего не меняет и никогда не влияет на маршрутизацию перевода. Оно попадает в ваши транзакции через поле правила
counterparty_dd_status: установите пресеты или напишите собственное правило на его основе. - Решение по бренду распространяется на каждую из его сущностей, у которой нет собственного решения, а в
inherited_fromсущность показывает бренд. Решение по сущности переопределяет решение по бренду для этой сущности. GETвозвращает действующее решение или{"decision": null}.DELETEудаляет ваше собственное решение и возвращает то, что действует после этого: решение по бренду для сущности, а иначеnull. Он отвечает404, если у вашего приложения нет собственного решения по контрагенту.reasonнеобязательно, до 255 символов, и каждыйPUTзаменяет его.decided_byуказывает пользователя консоли, который принял или последним изменил решение; поле пустое, если решение принял API-ключ.
Скрининг контрагента
Скрининг контрагента проверяет саму компанию VASP на наличие санкций и негативных публикаций в СМИ. Он включается явно и тарифицируется: включите его параметромcounterparty_screening_enabled в настройках. Пока он выключен, проверки не выполняются и не тарифицируются.
- Оплата. Каждый новый скрининг стоит цену 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/ отправляет её на адрес комплаенса, записанный для контрагента, вместе с одноразовой ссылкой на страницу ответов:
- Куда отправляется.
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 содержит следующие двенадцать вопросов:
Переводы с одним контрагентом
GET /v3/travel-rule/transfers/?counterparty={vasp_id} выводит переводы по Travel Rule вашего приложения с одним контрагентом, от новых к старым, по id, который показывает справочник: ваши переводы, контрагентом которых является эта запись справочника, или ваши обмены INTERNAL с этим участником сети Didit. Значение, которое не является допустимым id, возвращает 400.
Каждая строка содержит reason сразу после status: reason самого перевода. Так список позволяет отличить письмо комплаенса, которое не было доставлено, от контрагента, до которого нет маршрута, не открывая каждый перевод.
Политики переходного периода и контрагентов, не отвечающих на запросы
Когда срок перевода истекает (прошлоconfirmation_timeout_hours, пока он находился в статусе AWAITING_COUNTERPARTY), timeout_outcome определяет, что происходит с его транзакцией. Две настройки позволяют по-разному обрабатывать две распространённые причины тайм-аута:
HOLD, REJECT или PROCEED с тем же значением, что и для timeout_outcome, либо пустую строку, чтобы следовать timeout_outcome. Обе пустые по умолчанию, поэтому ничего не меняется, пока вы не зададите одну из них:
sunrise_policy, если она задана и юрисдикция контрагента входит вsunrise_jurisdictions.non_responsive_policy, если она задана и контрагент имеет статусUNRESPONSIVE.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.
Поля правил и пресеты
Четыре поля правила содержат то, что ваше приложение знает о контрагенте, до которого дошёл обмен Travel Rule по транзакции: его запись в справочнике или, для обменаINTERNAL, клиента Didit, который его получил. Каждое поле пустое, если обмен не дошёл до известного контрагента.
travel_rule работают с этими полями. Установите их в Transactions → Rules → Library в Business Console или с помощью API библиотеки правил:
Webhooks
Настройте адрес назначения webhook для подписки наtravel_rule.status.updated, чтобы получать уведомление о каждом переходе статуса каждого исходящего и входящего перевода в вашем приложении, включая части переводов на канале INTERNAL.
Маршрутизация выполняется после того, как ответ на создание транзакции уже возвращён, поэтому такие исходы, как COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, или обмен на INTERNAL, который сразу завершается, приходят к вам через это событие, а не в ответе на создание.
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, содержит rail: "TEST_VASP", test_vasp: true и test_vasp_scenario.
Полный каталог событий, проверку подписи и политику повторных попыток см. в руководстве по webhook.
Цены
Сбор за Travel Rule составляет $0.02 за каждый исходящий обмен, который начинается, и списывается однократно при создании транзакции, независимо от того, сколько переходов статуса обмен проходит потом. Повторная отправка (resend) никогда не списывает его снова.
- Он применяется к исходящей транзакции
travelRuleв приложении с включённым Travel Rule, сумма которой равна или превышает вашthreshold_amount, поэтому Didit запускает для неё управляемый обмен. Перевод на некастодиальный кошелёк или на собственный верифицированный кошелёк пользователя также подпадает под это правило: доказательство владения и есть его обмен. - Каждая другая исходящая транзакция, которая содержит данные Travel Rule, тарифицируется как стандартный Transaction Screening. Ваш тариф включает его до месячного лимита транзакций, а сверх лимита он стоит $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 за вызов). Ваш провайдер выставляет счёт за базовый запрос напрямую вам.
- Неуспешные запросы никогда не тарифицируются. Повторные отправки переиспользуют записанную атрибуцию без нового вызова и списания, а приложения в песочнице никогда не тарифицируются.
Пакет правил EU TFR
В пакетtravel_rule_eu_tfr входят четыре предустановленных правила, созданных специально для обязательств по EU TFR.
Как и любой другой предустановленный пакет, включите его в Business Console в разделе Transactions → Rules → Library.
Почему это работает лучше на одной платформе
Соответствие Travel Rule по сути является задачей идентификации в платёжной оболочке, и Didit уже располагает этими данными:- Проверенные данные уже у вас. Субъект каждого перевода связан через
vendor_dataс KYC-профилем, который Didit верифицировал при онбординге (OCR документа, проверка живости, сравнение лиц). Поэтому вы можете формироватьoriginator_dataиз проверенных полей на той же платформе. Для пользователя с одобренной KYC-верификацией Didit передаёт проверенную идентичность, а не значения, которые вы ввели вручную (см. Проверенные данные и их происхождение). - Биометрическая проверка для рискованных переводов. Правило может удерживать любой перевод на крупную сумму до тех пор, пока пользователь не пройдёт повторную биометрическую проверку по лицу, снятому при онбординге. Это самый надёжный ответ на захват учётной записи в момент вывода средств. См. пример с разбором.
- Один граф устройств. Переводы, отправленные через SDK, содержат те же данные об устройстве, что и KYC-сессии, поэтому правило «новое устройство + крупная сумма + первый получатель» пишется одним правилом, а не соединением таблиц в хранилище данных.
- Скрининг, правила, кейсы и обмен в одном месте. Скрининг кошелька, который определяет VASP контрагента, это тот же скрининг, который видят ваши правила и аналитики: одно списание, одна консоль, один журнал аудита.
- Опубликованные цены. $0.02 за исходящий обмен, без тарифов, зависящих от объёма, и без звонка в отдел продаж.
FAQ
Как работает обмен
Didit сам проводит обмен между VASP, или он только записывает данные Travel Rule после того, как другая сеть завершит обмен подтверждениями?
Didit сам проводит обмен между VASP, или он только записывает данные Travel Rule после того, как другая сеть завершит обмен подтверждениями?
travelRule, Didit выполняет поиск контрагента в реальном времени, передаёт данные отправителя и получателя принимающей VASP по соответствующим каналам и присваивает переводу статус COMPLETED только после того, как сторона контрагента подтвердит это - это управляемый обмен подтверждениями, а не журнал после расчёта.
Ответ контрагента обрабатывается по всем каналам, кроме TRUST, который только отправляет, поэтому перевод по нему истекает, а не завершается. По GTR, VERIFYVASP и SYGNA Didit получает ответ из ответа сети или опрашивает её каждые 5 минут, и ни один обмен по этим каналам пока не был завершён в работающей сети. По TRP, если контрагент не отвечает сразу, нужен публичный хост TRP Didit, который пока не запущен (см. Сети Travel Rule).
Didit также хранит полную запись и статус соответствия каждого обмена (жизненный цикл из 13 статусов, webhook и журнал аудита).
Если вы предпочитаете вести Travel Rule в собственном процессе, Didit может вместо этого записывать данные пассивно как вашу единственную систему учёта (см. вопрос о пассивной записи ниже).
Доступны оба режима: активный обмен запускается, когда вы задаёте is_enabled: true в настройках (по умолчанию false). До этого Didit записывает данные пассивно.Обмен нативный для Didit или зависит от внешнего провайдера взаимодействия (Notabene, Sygna, VerifyVASP, TRP)?
Обмен нативный для Didit или зависит от внешнего провайдера взаимодействия (Notabene, Sygna, VerifyVASP, TRP)?
trp.didit.me после запуска публичного хоста (пока он не отвечает, контрагенты не могут связаться с вами по TRP), и напрямую реализует сетевые протоколы отрасли: GTR (Global Travel Rule), TRUST, VerifyVASP и Sygna Bridge (поддерживается нативно, включая зашифрованный ECIES формат IVMS 101 от Sygna и подписанные запросы на разрешение). Didit не является перепродавцом одного стороннего уровня взаимодействия. Связь с контрагентом, который работает в конкретной сети членства, обязательно проходит через эту сеть, потому что в Travel Rule нет единого универсального канала. Однако поиск, обмен подтверждениями, проверку и ведение записей выполняет Didit.
Сегодня для каждого сетевого канала нужно собственное членство, а TRUST только отправляет: см. Обзор.Какие сети и протоколы Travel Rule поддерживает Didit?
Какие сети и протоколы Travel Rule поддерживает Didit?
INTERNAL (обе VASP на Didit), TRP (OpenVASP Travel Rule Protocol), GTR, TRUST, VerifyVASP, Sygna Bridge и резервный канал EMAIL для контрагентов без инструментов.
Сетевым каналам сегодня нужно ваше собственное членство, TRUST только отправляет, а приём через сеть пока не включён: см. Сети Travel Rule.
CODE, TRISA, Notabene и Veriscope появляются в Marketplace Didit как плитки запроса доступа, интеграции с ними пока нет.
Полную таблицу маршрутизации см. в разделе Как Didit маршрутизирует обмен, а о том, до кого каждая сеть может дотянуться сегодня, см. в разделе Сети Travel Rule.Какую сеть мне использовать и нужно ли мне собственное членство?
Какую сеть мне использовать и нужно ли мне собственное членство?
TRP - базовый вариант, который мы рекомендуем: для него не нужно общее членство. Если вы делитесь своим Travel Address, с вами может связаться любая VASP, которая поддерживает TRP. Когда контрагент известен только по адресу кошелька, атрибуция по блокчейн-аналитике направляет его по TRP только в том случае, если у записи каталога с атрибуцией уже есть конечная точка TRP.
При исходящих переводах Didit отправляет по этому каналу активы из списка идентификаторов активов TRP. Если контрагент не отвечает сразу, нужен публичный хост TRP Didit, который пока не запущен.
Для сетей членства выбирайте по тому, где работают ваши контрагенты: GTR обеспечивает широкое глобальное покрытие, TRUST ориентирован на VASP из США, а VerifyVASP (который теперь также управляет Sygna Bridge) имеет широкое покрытие в Азии.
Да, сегодня вам нужно собственное членство: у Didit пока нет членства платформы в этих сетях, и мы подключаемся как провайдер.
Подключите своё членство в Marketplace. Вы не привязаны к одной сети.
Помните, что TRUST только отправляет, поэтому перевод по нему не завершается, а ни один обмен по сети пока не был завершён в работающей сети.Как Didit находит VASP контрагента, если у меня есть только адрес кошелька?
Как Didit находит VASP контрагента, если у меня есть только адрес кошелька?
INTERNAL), затем ваша адресная книга кошельков с сохранённым Travel Address, затем протокольные и сетевые каналы и, наконец, атрибуция по блокчейн-аналитике.
Если перевод содержит только адрес кошелька назначения, Didit сверяет его с данными блокчейн-аналитики, определяет биржу или кастодиальный сервис контрагента и заносит его в каталог VASP.
Перевод автоматически маршрутизируется только в том случае, если у записи каталога уже есть маршрут, например конечная точка TRP. Иначе он завершается статусом COUNTERPARTY_VASP_NOT_FOUND с сохранённым именем контрагента. Вы можете отправить его повторно, когда появится маршрут, например после того, как сохраните travel_address контрагента в записи адресной книги для этого кошелька (для активов из списка идентификаторов активов TRP).Цены и лицензирование
Сколько стоит Travel Rule и входит ли она в мой план KYC или верификации?
Сколько стоит Travel Rule и входит ли она в мой план KYC или верификации?
Будет ли списано с меня дважды, если работают Travel Rule и мониторинг криптовалют?
Будет ли списано с меня дважды, если работают Travel Rule и мониторинг криптовалют?
travelRule оплачивается одной строкой: платой за Travel Rule ($0.02), когда она начинает исходящий обмен, и стандартной проверкой транзакций в остальных случаях. Запрос к блокчейн-аналитике оплачивается один раз. Если мониторинг криптовалют уже проверил кошелёк назначения в этой транзакции, атрибуция использует сохранённый результат без дополнительной оплаты.
Входящие части переводов, которые Didit создаёт на стороне получателя, никогда не оплачиваются платой за Travel Rule.Может ли Didit предоставить документацию или описание возможностей для моего досье по лицензированию VASP или для регулятора?
Может ли Didit предоставить документацию или описание возможностей для моего досье по лицензированию VASP или для регулятора?
Операции и данные
Что происходит с переводами ниже моего порога?
Что происходит с переводами ниже моего порога?
NOT_APPLICABLE: канал не задействуется, и данные об идентификации не покидают Didit.
Транзакция тарифицируется как обычный Transaction Screening, а не по тарифу Travel Rule.
Didit сравнивает порог с суммой, пересчитанной в валюту приложения по умолчанию.
Если Didit не может пересчитать сумму, порог не применяется, и перевод обрабатывается как обмен данными по Travel Rule.
У регламента EU TFR нет минимальной суммы, поэтому CASP из ЕС должны оставлять threshold_amount равным 0.00.Что, если контрагент никогда не отвечает?
Что, если контрагент никогда не отвечает?
AWAITING_COUNTERPARTY, пока не истечёт confirmation_timeout_hours. Затем планировщик переводит его в EXPIRED и применяет ваш timeout_outcome к базовой транзакции: HOLD для проверки, REJECT для отклонения или PROCEED для оставления без изменений. Предустановленное правило tr_eu_expired_review дополнительно выводит каждое истечение срока для ваших аналитиков. Ситуация, когда контрагент вообще не принимает сообщение, другая: Didit повторяет попытку, а после шестой неудачной попытки перевод переходит в COUNTERPARTY_VASP_NOT_REACHABLE (см. Доставка и повторные попытки).У моего контрагента вообще нет инструментов Travel Rule. Могу ли я всё равно соблюсти требования?
У моего контрагента вообще нет инструментов Travel Rule. Могу ли я всё равно соблюсти требования?
EMAIL. Контактное лицо контрагента по комплаенсу получает одноразовую ссылку на получение и может подтвердить или отклонить обмен на размещённой странице без аккаунта Didit, без API-ключа и без интеграции. Для депозитов, которые были зачислены до того, как обмен стал возможен, используйте регистрацию sunrise после депозита.Как работают выводы на собственный кошелёк пользователя?
Как работают выводы на собственный кошелёк пользователя?
payment_method.method_type: "unhosted_wallet"). Didit пропускает маршрутизацию через VASP и вместо этого собирает подтверждение того, что ваш клиент контролирует кошелёк: подписание сообщения, тест Satoshi, скриншот или самодекларацию, через виджет подтверждения владения кошельком. Это требование EU TFR к некастодиальным кошелькам в виде API.Защищены ли персональные данные контрагента во время обмена?
Защищены ли персональные данные контрагента во время обмена?
GTR, TRUST, VERIFYVASP, SYGNA) дополнительно шифруют payload IVMS открытым ключом принимающего VASP.Я уже отслеживаю соответствие Travel Rule своим процессом. Могу ли я просто записать это?
Я уже отслеживаю соответствие Travel Rule своим процессом. Могу ли я просто записать это?
travel_rule_details, которые вы передаёте (включая переданный вами status, текущий или устаревший), записываются в транзакцию пассивно: рельс не запускается, ничего не согласовывается. Используйте это, чтобы внести данные задним числом или вести собственный процесс, сохраняя одну основную систему учёта.Как протестировать это перед запуском?
Как протестировать это перед запуском?
transaction.created и transaction.status.updated не отправляются, и каждый исходящий обмен направляется в Didit Test VASP по каналу TEST_VASP. Тестовый VASP отвечает внутри Didit по выбранному вами сценарию: принятие, отклонение, несовпадение имени, недоступность, тайм-аут или некастодиальный кошелёк. Вы получаете вебхуки travel_rule.status.updated с пометкой environment: sandbox, и ни одно сообщение не доходит до реального контрагента.
Для входящих переводов попросите тестовый VASP отправить вам перевод с помощью Trigger Test VASP Inbound Transfer или зарегистрируйте его через POST /v3/travel-rule/inbound/. В обоих случаях решение принимается на основе адресной книги ваших кошельков: ON_HOLD, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_MISMATCHED_DATA либо COMPLETED, а затем FINISHED.
Для двустороннего обмена с другим реальным приложением используйте два своих продакшн-приложения по каналу INTERNAL: принимающее приложение должно быть доступно для обнаружения другими приложениями и доказать контроль над адресом назначения кошелька подписанным сообщением или тестовым депозитом.