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

# API Travel Rule GAFI : conformité VASP

> Travel Rule GAFI avec Didit pour VASP et PSP : échange des données du donneur d'ordre et du bénéficiaire, suivi des protocoles. $0.02 par échange.

Didit assure l'échange d'informations entre donneur d'ordre et bénéficiaire prévu par le règlement sur le transfert de fonds (TFR) du GAFI et de l'UE pour les transferts d'actifs virtuels. Il recherche un canal qui atteint le VASP de la contrepartie, y négocie les données requises, suit l'échange au cours d'un cycle de vie à 13 états, et vous indique quand l'échange est terminé afin que vous puissiez libérer les fonds on-chain.

## Vue d'ensemble

La règle de voyage du GAFI (Recommandation 16), mise en œuvre dans l'UE sous le nom de règlement sur les transferts de fonds (TFR), oblige le prestataire de services sur actifs virtuels (VASP) émetteur, appelé **VASP du donneur d'ordre**, à collecter et transmettre les informations sur le donneur d'ordre et le bénéficiaire au VASP récepteur, appelé **VASP du bénéficiaire**, avant que le transfert d'actifs virtuels ne soit réglé.

Contrairement à la plupart des juridictions, le TFR de l'UE s'applique **sans montant minimum** : chaque transfert de cryptoactifs entre dans son champ, et pas seulement les transferts au-dessus d'un seuil de déclaration.

Le produit Travel Rule de Didit est un échange géré, et non un simple champ de données :

* Vous soumettez un transfert avec `transaction_category: "travelRule"`.
* Didit essaie les canaux dans l'ordre de priorité pour atteindre le VASP de la contrepartie, exécute l'échange sur le premier qui l'atteint et, lorsque l'autre partie renvoie un nom de bénéficiaire, le compare à celui que vous avez envoyé.
* Ce n'est qu'une fois que l'échange atteint `COMPLETED` que vous devez diffuser le transfert on-chain.
* Vous renvoyez ensuite le hash de la transaction on-chain à Didit pour clôturer l'échange avec le statut `FINISHED`.

Sept canaux sont livrés dans cette version, essayés dans un ordre de priorité strict : le premier capable d'atteindre la contrepartie l'emporte :

| Priorité | Canal | Fonctionnement |
| - | - | - |
| 1 | `INTERNAL` | Utilisé lorsqu'un autre client de Didit a prouvé le contrôle du portefeuille bénéficiaire au moyen d'un message signé ou d'un dépôt de test. Didit crée un transfert entrant correspondant sur l'application de ce client et négocie l'échange entièrement au sein de la plateforme, sans appel réseau sortant. Instantané. |
| 2 | `TRP` | Intégration native avec le protocole ouvert Travel Rule Protocol (TRP / OpenVASP). Utilisé lorsque le transfert comporte une [Travel Address](#travel-addresses), que l'entrée du carnet d'adresses du portefeuille de destination en stocke une, ou que le VASP de la contrepartie se résout dans l'[annuaire](#annuaire) avec un endpoint TRP. Couvre les actifs listés sous [identifiants d'actifs TRP](#identifiants-d’actifs-trp). Didit échange les données du donneur d'ordre et du bénéficiaire de machine à machine, sans aller-retour par e-mail. |
| 3 | `GTR` | Réseau Global Travel Rule (GTR). La contrepartie est résolue par un `counterparty_gtr_vasp_code` explicite ou par une entrée d'annuaire qui en comporte un. Didit lit la réponse de la contrepartie dans la réponse de GTR, ou en interrogeant le statut de la requête toutes les 5 minutes pendant que le transfert est en attente. |
| 4 | `TRUST` | Réseau TRUST. La contrepartie est résolue par `counterparty_trust_membership_id` ou par une entrée d'annuaire qui en comporte un. Envoi uniquement : TRUST ne renvoie rien que Didit puisse lire, si bien que le transfert reste en `AWAITING_COUNTERPARTY` jusqu'à son expiration. |
| 5 | `VERIFYVASP` | Réseau VerifyVASP. La contrepartie est résolue par `counterparty_verifyvasp_id` ou par une entrée d'annuaire qui en comporte un. Didit lit la réponse de la contrepartie en interrogeant le statut de la vérification toutes les 5 minutes. |
| 6 | `SYGNA` | Réseau Sygna Bridge (racheté par VerifyVASP en 2026, toujours en service pour les membres existants). Implémentation native du protocole Sygna : IVMS101 chiffré en ECIES avec des demandes d'autorisation signées. La contrepartie est résolue par `counterparty_sygna_vasp_code` ou par une entrée d'annuaire qui en comporte un. La réponse d'acceptation ou de refus de la contrepartie finalise ou refuse le transfert : Didit interroge le statut du transfert toutes les 5 minutes, et reçoit aussi la réponse via son callback Sygna dès que l'hôte public TRP de Didit sera en ligne. |
| 7 | `EMAIL` | Solution de repli utilisée lorsqu'aucun canal de protocole ou de réseau n'atteint la contrepartie et que vous indiquez son contact de conformité dans `counterparty_vasp_email`, ou que l'entrée d'annuaire de la contrepartie comporte un [contact de conformité](#contacts). Didit envoie à ce contact un lien d'accès à usage unique lui permettant de confirmer ou de refuser l'échange sans compte Didit. C'est le principal outil face au problème du « sunrise », soit les contreparties qui ne sont pas encore joignables sur aucun réseau. |

Les canaux réseau (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`) nécessitent une adhésion au réseau côté émetteur, et aujourd'hui cette adhésion doit être **la vôtre**.
Connectez vos identifiants d'adhésion dans la **Marketplace** de la console (catégorie Travel Rule networks), et vos transferts passent alors sous votre propre identité réseau.

**Didit ne détient aujourd'hui aucune adhésion de plateforme à l'un des quatre réseaux, et est en cours d'onboarding en tant que prestataire auprès d'eux.**
Tant que cet onboarding n'est pas terminé, `platform_available` vaut `false` pour les quatre, le mode `didit` des paramètres (routage via l'adhésion de plateforme de Didit) n'est disponible pour aucun réseau, et `disabled_networks` n'a rien à désactiver.
Une adhésion que vous connectez vous-même a toujours la priorité sur une adhésion de plateforme.
Aucune adhésion réseau n'est incluse par défaut, GTR compris.
Activer la Travel Rule n'active ni GTR, ni TRUST, ni VerifyVASP, ni Sygna.
Consultez [Get Travel Rule Settings](https://docs.didit.me/fr/management-api/travel-rule/get-settings.md) pour les modes `networks` effectifs de votre application, et [Travel Rule networks](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md) pour connaître les contreparties que chaque réseau peut atteindre aujourd'hui.
Si les quatre renvoient `unavailable`, aucun ne peut router un échange : connectez votre propre adhésion dans la **Marketplace**, ou utilisez une contrepartie joignable via `INTERNAL`, `TRP` ou `EMAIL`.

Lors de l'enregistrement d'une adhésion, son code de participant doit être le vôtre : l'enregistrement renvoie `400` lorsque le code est le code propre de Didit sur ce réseau, ou un code déjà détenu par l'adhésion d'une autre application.
Une adhésion GTR nécessite sa clé privée ed25519 (en base64), celle dont la clé publique est enregistrée dans le portail de GTR : elle chiffre les données personnelles que vous envoyez et déchiffre celles que vous recevez.
Une adhésion VerifyVASP pointe vers votre propre Enclave VerifyVASP : son URL est celle de votre Enclave, et son token est celui que votre Enclave présente à Didit.

Même avec votre propre adhésion, un canal réseau ne route un transfert que si son `beneficiary_data` contient l'identifiant de la contrepartie sur ce réseau (`counterparty_gtr_vasp_code`, `counterparty_trust_membership_id`, `counterparty_verifyvasp_id` ou `counterparty_sygna_vasp_code`), ou si l'entrée d'annuaire de la contrepartie le répertorie sur ce réseau (voir [Routes](#routes)).

`platform_available` indique si l'adhésion de plateforme est configurée, et non un contrôle de santé du réseau en direct. De même, `connected` signifie que vos identifiants d'adhésion sont enregistrés et ont passé la validation locale ; le premier échange en direct les vérifie auprès du réseau. Un mode `didit` ou `own` ne garantit ni l'accessibilité de la contrepartie ni la réussite de la livraison.

Avec votre propre adhésion, Didit lit la réponse de la contrepartie via `GTR`, `VERIFYVASP` et `SYGNA` à partir de la réponse du réseau ou en interrogeant le réseau toutes les 5 minutes, ce qui finalise ou refuse le transfert.
`TRUST` ne fait qu'envoyer : un transfert envoyé par ce biais reste en `AWAITING_COUNTERPARTY` jusqu'à l'écoulement de `confirmation_timeout_hours`, puis passe en `EXPIRED` et votre `timeout_outcome` s'applique.
La réception d'un transfert qu'un autre VASP vous envoie par l'intermédiaire d'un réseau n'est pas encore activée, et aucun échange par l'intermédiaire d'un réseau n'a encore été finalisé sur un réseau en production : voir [Réseaux Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md).

D'autres réseaux - CODE (l'alliance coréenne), TRISA, Notabene et Veriscope - sont listés dans la **Marketplace** de la console sous forme de vignettes de demande d'accès.
Didit n'a pas encore d'intégration avec eux, si bien qu'aucun transfert ne peut être acheminé par leur intermédiaire aujourd'hui.

Lorsqu'aucun des sept canaux ne parvient à identifier une contrepartie, Didit effectue une étape de résolution supplémentaire avant d'abandonner : l'attribution par analyse de la blockchain.
Le portefeuille de destination est recherché dans les données d'analyse de la blockchain ; lorsqu'il appartient à une plateforme d'échange ou à un dépositaire connu, Didit enregistre le VASP attribué sur le transfert, l'ajoute à l'[annuaire](#annuaire) et relance les canaux une fois avec le nouveau nom.
Voir [Attribution par analyse de la blockchain](#attribution-par-analyse-de-la-blockchain) pour le comportement complet et la tarification.

Un transfert dont la destination est [déclarée comme portefeuille auto-hébergé](#portefeuilles-auto-hébergés-déclarés) ignore les sept canaux - il n'existe aucun VASP de contrepartie avec lequel échanger - et passe directement à la preuve de propriété du portefeuille par l'utilisateur final.

> [!NOTE]
>
> Didit assure aussi le volet de l'échange destiné à la contrepartie : les endpoints TRP publics d'identité et de demande que les autres VASP appellent pour vous joindre, les endpoints de rappel et le flux de récupération par e-mail.
> Vous ne les appelez jamais vous-même - il s'agit d'une infrastructure gérée - mais la page [Interopérabilité Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-interoperability.md) documente chacun d'entre eux afin que les VASP des contreparties puissent se connecter à vous.
> Vous n'interagissez avec Travel Rule que par les endpoints `/v3/` décrits sur cette page.

## Ce qui s'exécute lors de l'envoi d'un transfert

Les contrôles s'exécutent dans l'unique appel `POST /v3/transactions/` : au moment où vous recevez la réponse, le filtrage a été effectué, le transfert est enregistré et les règles ont été évaluées.
L'échange lui-même commence après la réponse : Didit choisit le rail et contacte la contrepartie en dehors de votre requête, de sorte qu'aucun réseau ni aucune contrepartie ne peut ralentir ou faire échouer la création.
Chaque changement de statut ultérieur arrive sous forme d'un webhook [`travel_rule.status.updated`](#webhooks) qui indique le rail. L'objet transfert renvoyé par les endpoints de transfert Travel Rule contient un champ [`delivery`](#livraison-et-nouvelles-tentatives) qui indique si le message a bien été transmis.
Il n'existe aucun pipeline à interroger : les parties asynchrones sont l'échange, la réponse de la contrepartie et toute action de l'utilisateur final.

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

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

Étapes :

1. **Validation et rattachement du sujet.** La charge utile est validée.
   Un `transaction_id` déjà utilisé dans votre application est rejeté avec `400`, et lorsque deux requêtes portant le même identifiant arrivent en même temps, l'une d'elles reçoit `409`.
   Le `subject` est rattaché à votre profil utilisateur ou entreprise via `vendor_data`, de sorte que le transfert apparaît sur ce profil pour la surveillance.
   Un sujet ou un `vendor_data` de contrepartie figurant sur une liste de blocage est rejeté avec `400`, et aucune transaction n'est créée.
   Les portefeuilles, comptes bancaires, adresses IP et appareils figurant sur une liste de blocage sont vérifiés à l'étape 6 et refusent la transaction à ce stade, après l'exécution du routage.
2. **Enrichissement de l'appareil et du réseau.** Les adresses IP des parties sont enrichies de données de géolocalisation, et tout contexte d'appareil (automatique lors d'une [soumission via le SDK](https://docs.didit.me/fr/transaction-monitoring/sdk-transaction-submission.md)) est normalisé dans le graphe des appareils. C'est le même graphe qu'alimentent les sessions KYC, de sorte que le critère « lié par appareil » fonctionne à travers l'onboarding et les transactions.
3. **Filtrage crypto.** Pour les transferts crypto avec la surveillance activée, le portefeuille concerné (ou le hash on-chain) est filtré auprès des outils d'analyse blockchain avant tout échange : exposition aux sanctions, proximité avec un mixeur, fonds volés. Voir [Filtrage crypto et des portefeuilles](https://docs.didit.me/fr/transaction-monitoring/aml-screening.md).
4. **Transfert Travel Rule.** Le moteur d'échange vérifie le seuil et tient compte d'une [déclaration de portefeuille auto-hébergé](#portefeuilles-auto-hébergés-déclarés), puis enregistre le transfert : `NOT_APPLICABLE` sous votre seuil, `UNCONFIRMED_OWNERSHIP` pour un portefeuille auto-hébergé déclaré, `COMPLETED` lorsque l'autre portefeuille est votre portefeuille vérifié, et `AWAITING_COUNTERPARTY` dans les autres cas, avec un rail encore vide.
   Chaque changement de statut met en file d'attente un événement `travel_rule.status.updated`, et Didit l'envoie après le `transaction.created` de la transaction (étape 7).
5. **Création de la session de preuve de propriété du portefeuille.** Si le transfert exige une preuve de contrôle par l'utilisateur final et que `auto_wallet_verification` est activé, une session du [widget de preuve de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) est créée et renvoyée dans `action_required`.
6. **Évaluation des règles.** Toutes les règles actives s'exécutent : le [ensemble EU TFR](#ensemble-de-règles-eu-tfr) réagit au statut du transfert défini à l'étape 4, vos règles personnalisées tiennent compte du résultat du filtrage de l'étape 3, et une règle peut bloquer la transaction (`IN_REVIEW`), la refuser ou exiger une [vérification renforcée](https://docs.didit.me/fr/transaction-monitoring/sdk-transaction-submission.md#actions-requises-de-l’utilisateur) (`AWAITING_USER`).
7. **Facturation et notification.** Une transaction sortante est facturée sur une seule ligne : la [ligne Travel Rule](#tarifs) lorsqu'elle lance un échange, et Transaction Screening dans le cas contraire. Un transfert entrant ne génère aucun de ces frais, et une application sandbox n'est jamais facturée.
   La réponse est renvoyée, `transaction.created` est envoyé à vos destinations de webhook, suivi des événements `travel_rule.status.updated` de l'étape 4.
8. **Routage, après la réponse.** Didit parcourt les [sept rails](#vue-d’ensemble) par ordre de priorité. Si aucun ne permet de résoudre la contrepartie, l'[attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain) identifie la contrepartie et les rails sont relancés une fois. Le premier rail accessible achemine l'échange : le transfert reste `AWAITING_COUNTERPARTY` sur ce rail tant que la contrepartie ne répond pas, est réglé immédiatement lorsqu'une contrepartie `INTERNAL` correspondante est trouvée (`COMPLETED` ou `COUNTERPARTY_MISMATCHED_DATA`), ou passe à `COUNTERPARTY_VASP_NOT_FOUND` lorsqu'aucun rail n'atteint la contrepartie. Chaque changement déclenche `travel_rule.status.updated`, et vos règles sont à nouveau évaluées dès que le statut ou le rail change. Un transfert que le routage laisse en `COUNTERPARTY_VASP_NOT_FOUND` reçoit à ce moment sa session de widget de preuve de propriété du portefeuille (lorsque `auto_wallet_verification` est activé). Vous la lisez dans `action_required` de la transaction avec [Get transaction](https://docs.didit.me/fr/management-api/transactions/get.md).

### Comment chaque étape correspond à vos obligations

| Étape du pipeline | Obligation remplie |
| - | - |
| Données de l'initiateur que vous fournissez dans `originator_data` | Exactitude des informations sur l'initiateur (FATF R.16 ; EU TFR art. 14). Sur les rails qui transmettent des données personnelles (`INTERNAL`, `TRP` et les rails réseau), Didit transmet l'identité vérifiée lorsque le sujet est un utilisateur disposant d'une vérification KYC Didit approuvée (voir [Données vérifiées et provenance](#données-vérifiées-et-provenance)), et sinon exactement les `originator_data` que vous envoyez. |
| Échange sur les rails avant le règlement | Informations sur l'initiateur et le bénéficiaire transmises au VASP de la contrepartie **avant** le règlement du transfert (FATF R.16 ; EU TFR art. 14 à 16) |
| Correspondance du nom du bénéficiaire | Vérification que les données du bénéficiaire correspondent à la destination (obligations du CASP du bénéficiaire, EU TFR art. 16) |
| Preuve de propriété du portefeuille auto-hébergé | L'obligation EU TFR relative aux portefeuilles auto-hébergés : vérifier la propriété d'une adresse non hébergée au lieu d'échanger avec un VASP |
| Filtrage crypto + ensemble de règles EU TFR | Procédures fondées sur les risques pour les transferts dont les informations sont manquantes ou incomplètes, et contrôles de l'exposition aux sanctions |
| Verrou `COMPLETED` avant la diffusion | Les fonds ne sont pas libérés tant que l'échange n'est pas conclu : c'est le cœur de la règle |
| Clôture `FINISHED` avec le hash on-chain et la chronologie | Conservation des enregistrements : chaque transfert conserve la chronologie complète de ses échanges pour l'audit |

> [!NOTE]
>
> **Note sur les tests.** Les applications sandbox ignorent l'évaluation des règles et la facturation, et n'envoient aucun webhook `transaction.created` ni `transaction.status.updated`. Les transactions sandbox restent `APPROVED`, sauf si un transfert Travel Rule arrivant à expiration applique votre `timeout_outcome`, et rien n'est facturé.
> Le moteur d'échange Travel Rule fonctionne lui-même sur les applications sandbox lorsque Travel Rule est activé, avec une seule contrepartie : chaque échange **sortant** est dirigé vers la [Didit Test VASP](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) sur le rail `TEST_VASP`, qui répond à l'intérieur de Didit selon le scénario que vous choisissez. Le transfert peut ainsi être complété, refusé, présenter une incohérence, devenir injoignable, expirer ou attendre la preuve de propriété du portefeuille.
> Aucun message de conformité n'atteint jamais un VASP de contrepartie réelle, et les webhooks `travel_rule.status.updated` sont envoyés avec `environment: sandbox`.
> **Entrant** : le VASP de test peut vous envoyer un transfert ([Trigger Test VASP Inbound Transfer](https://docs.didit.me/fr/management-api/travel-rule/trigger-test-vasp-inbound.md)), et l'enregistrement (`POST /v3/travel-rule/inbound/`) est réglé en fonction de votre carnet d'adresses de portefeuilles comme sur une application en production : `ON_HOLD`, `UNCONFIRMED_OWNERSHIP`, `COUNTERPARTY_MISMATCHED_DATA`, ou `COMPLETED` puis `FINISHED`.
> Voir [Tester Travel Rule en sandbox](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md).

## Cycle de vie

Chaque transfert a un `status` parmi 13 valeurs. L'échange commence à `AWAITING_COUNTERPARTY` dans le cas normal, à `NOT_APPLICABLE` lorsque le montant est inférieur à votre `threshold_amount` ou qu'une demande de paiement Lightning ne désigne aucun bénéficiaire (voir [Lightning](#lightning)), ou à `UNCONFIRMED_OWNERSHIP` lorsque la destination a été [déclarée comme portefeuille auto-hébergé](#portefeuilles-auto-hébergés-déclarés). Il se termine dans l'un des états finaux : `FINISHED`, `CANCELLED` ou `EXPIRED`.

Le transfert suit sa propre machine à états, **distincte du** statut d'examen de la transaction sous-jacente. Un statut d'échange ne modifie jamais à lui seul le statut de la transaction, à une seule exception près : une expiration applique votre `timeout_outcome` configuré (ou une [politique sunrise ou de non-réponse](#politiques-de-période-de-transition-et-d’absence-de-réponse) lorsqu'elle s'applique). Vos règles sont en revanche réévaluées à chaque transition : les [règles TFR de l'UE](#ensemble-de-règles-eu-tfr) ou toute règle portant sur `travel_rule_status` peuvent donc faire évoluer la transaction, par exemple vers `IN_REVIEW`. Un transfert qui requiert une preuve de contrôle du portefeuille par l'utilisateur final ajoute un bloc `action_required` à la transaction, mais son `status` reste tel que vos règles l'ont laissé. Lisez l'état de l'échange dans `travel_rule.status` ; lisez le verdict d'autorisation / de suspension / de refus dans le `status` propre de la transaction.

Chaque transition (et chaque statut signalé passivement) est également reflété dans les valeurs personnalisées de la transaction, afin que les [règles](https://docs.didit.me/fr/transaction-monitoring/rules.md) puissent s'y référer : `custom_values.travel_rule_status` (statut canonique), `custom_values.travel_rule_rail` (le rail qui a porté l'échange, vide tant qu'aucun rail n'est sélectionné) et `custom_values.travel_rule_regime` (dérivé du `jurisdiction` de vos [paramètres](#configuration) : `SG` → `singapore`, `GB` → `uk`, et tout code d'un État membre de l'UE ou de l'EEE, comme `DE` ou `FR`, → `eu` ; les codes non mappés sont inscrits en minuscules, par exemple `US` → `us`. Un `props.travel_rule_regime` que vous soumettez vous-même prime toujours sur la valeur dérivée).

| Statut | Signification |
| - | - |
| `AWAITING_COUNTERPARTY` | Le transfert attend sa contrepartie : d'abord pendant que Didit choisit le rail et envoie l'échange, juste après la création, puis pendant que la contrepartie répond. La [`delivery`](#livraison-et-nouvelles-tentatives) du transfert indique si le message a été acheminé. `TRUST` ne fait qu'envoyer, donc un transfert passant par ce rail reste dans cet état jusqu'à son expiration. |
| `UNCONFIRMED_OWNERSHIP` | Le transfert attend la preuve de contrôle du portefeuille. Cet état est atteint lorsque le portefeuille de la contrepartie a été [déclaré auto-hébergé](#portefeuilles-auto-hébergés-déclarés) à la soumission (sortant ou entrant), ou lorsqu'un transfert entrant correspond à une [entrée du carnet d'adresses](#carnet-d’adresses-de-portefeuilles) dont la propriété n'est pas encore vérifiée. Il progresse via le [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) ou [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille). |
| `ON_HOLD` | Didit ne définit cet état que pour les transferts entrants : le portefeuille de destination d'un [enregistrement après dépôt](#enregistrement-a-posteriori-sunrise) ou d'une demande `TRP` entrante ne figure pas dans votre [carnet d'adresses](#carnet-d’adresses-de-portefeuilles), ou une demande `TRP` entrante attend votre décision (l'acceptation automatique est désactivée, ou la réponse automatique n'a pas pu être envoyée). Résolvez-le avec [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille). Il ne modifie pas à lui seul le statut de la transaction ; la [règle de bibliothèque](https://docs.didit.me/fr/transaction-monitoring/rules-library.md) `travel-rule-pending-action`, à activer explicitement, fait passer ces transactions à `AWAITING_USER`. |
| `COMPLETED` | L'échange s'est conclu avec succès : la contrepartie (ou, sur un transfert entrant, vous) l'a approuvé, ou les contrôles effectués par Didit sur ce parcours ont réussi (correspondance du nom du bénéficiaire, preuve de contrôle du portefeuille). Le transfert peut être diffusé on-chain en toute sécurité. |
| `FINISHED` | L'échange est clos avec le hash de sa transaction on-chain. Sur un transfert sortant, vous transmettez ce hash via [`PATCH .../travel-rule/`](#envoyer-un-transfert). Sur un transfert entrant, Didit le définit lorsque le dépôt était déjà réglé ([enregistrement après dépôt](#enregistrement-a-posteriori-sunrise)), lorsque le donneur d'ordre confirme un transfert `TRP`, ou lorsque le côté sortant `INTERNAL` se termine. |
| `CANCELLED` | Annulé manuellement avec `action: "cancel"` depuis tout état non terminal, ou annulé par le donneur d'ordre d'un transfert `TRP` entrant. |
| `EXPIRED` | `confirmation_timeout_hours` s'est écoulé alors que le transfert était en `AWAITING_COUNTERPARTY`. Un processus automatique (toutes les 15 minutes) fait expirer le transfert et applique votre `timeout_outcome` configuré à la transaction sous-jacente : `REJECT` la refuse, `HOLD` la place en examen, `PROCEED` la laisse inchangée. Votre [politique sunrise ou de non-réponse](#politiques-de-période-de-transition-et-d’absence-de-réponse) remplace `timeout_outcome` lorsqu'elle s'applique. |
| `COUNTERPARTY_MISMATCHED_DATA` | Le nom du bénéficiaire ne satisfait pas le `name_matching_strictness` configuré par rapport à l'autre partie : le nom renvoyé par la contrepartie, ou le `holder_name` de l'entrée correspondante du carnet d'adresses. |
| `COUNTERPARTY_VASP_NOT_FOUND` | Ni les sept rails, ni le repli d'[attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain), n'ont pu identifier un VASP contrepartie joignable pour ce transfert. Toute entité attribuée est tout de même enregistrée sur le transfert et dans le répertoire. Sur les transferts sortants, Didit crée automatiquement une session de [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) (lorsque `auto_wallet_verification` est activé), afin que le transfert puisse tout de même aboutir comme transfert vers un portefeuille auto-hébergé. `reason` et `next_actions` indiquent la cause et les actions qui le font avancer : voir [Pourquoi un transfert est bloqué](#pourquoi-un-transfert-est-bloqué). |
| `COUNTERPARTY_VASP_NOT_REACHABLE` | Un VASP contrepartie a été identifié, mais le rail sélectionné n'a pas pu livrer : son endpoint d'identité ou de demande `TRP` est resté injoignable, l'identité qu'il a signalée ne correspond ni à `counterparty_vasp_lei` ni à `counterparty_vasp_name`, un réseau a refusé le message ou ne l'a pas pris en charge après six tentatives (voir [Livraison et nouvelles tentatives](#livraison-et-nouvelles-tentatives)), ou le fournisseur de messagerie a refusé l'e-mail de conformité. `reason` permet de distinguer ces cas, et `next_actions` indique ce qui fait avancer le transfert : voir [Pourquoi un transfert est bloqué](#pourquoi-un-transfert-est-bloqué). Réessayez avec `action: "resend"` dès que le VASP est de nouveau joignable. |
| `COUNTERPARTY_VASP_GENERAL_DECLINE` | La contrepartie a refusé l'échange : propriété refusée, prise en charge de l'e-mail refusée, ou rejet `TRP` ou Sygna. |
| `NOT_ENOUGH_COUNTERPARTY_DATA` | Une demande `TRP` entrante n'identifiait le bénéficiaire ni par son nom ni par son adresse, si bien que Didit n'a pas pu la faire correspondre à votre carnet d'adresses. Ni l'endpoint de propriété ni `resend` n'acceptent un tel transfert entrant : vous ne pouvez que l'annuler, et l'annulation ne répond pas à la demande de la contrepartie. Didit ne définit jamais cet état sur un transfert sortant ; si vous l'enregistrez vous-même sur un tel transfert, `action: "resend"` relance le routage. |
| `NOT_APPLICABLE` | Le montant du transfert était inférieur au `threshold_amount` configuré, une demande de paiement [Lightning](#lightning) sortante ne désigne aucun bénéficiaire (`chain_details.not_applicable_reason` en indique la raison), ou vous avez vous-même enregistré ce statut alors que la Travel Rule était désactivée (y compris les valeurs héritées `UNKNOWN` et `EXEMPT`, voir ci-dessous). |

> [!WARNING]
>
> **Ne diffusez jamais avant `COMPLETED`.**
> Libérer des fonds on-chain alors qu'un transfert est en `AWAITING_COUNTERPARTY`, `UNCONFIRMED_OWNERSHIP` ou dans un état de non-concordance ou de refus va à l'encontre de l'objectif de l'échange.
> Conditionnez votre étape de diffusion à `status == "COMPLETED"`, puis appelez `PATCH .../travel-rule/` avec le `payment_txn_id` obtenu pour passer à `FINISHED`.

Les transitions en un coup d'œil (`CANCELLED` est en outre accessible depuis chaque état non terminal avec `action: "cancel"`) :

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

### Livraison et nouvelles tentatives

Didit envoie séparément de la requête qui l'a déclenché chaque message qu'un transfert doit transmettre, et le réessaie jusqu'à ce que l'autre partie le prenne en charge.
L'objet de transfert renvoyé par [Terminer, annuler ou renvoyer](https://docs.didit.me/fr/management-api/travel-rule/update-transfer.md), [Confirmer la propriété](https://docs.didit.me/fr/management-api/travel-rule/confirm-ownership.md) et [l'enregistrement sunrise](#enregistrement-a-posteriori-sunrise) contient le message le plus récent dans `delivery`, et c'est aussi le cas de l'objet `travel_rule` intégré à la transaction ; il vaut `null` lorsque le transfert n'a jamais eu de message à envoyer.

| Champ | Signification |
| - | - |
| `kind` | `SEND` (le premier message envoyé à la contrepartie), `ANSWER` (votre acceptation ou votre refus, dû au réseau qui a fait la demande), `TXID` (le hachage on-chain, après que vous avez terminé le transfert), `CANCEL` (après que vous l'avez annulé) ou `POLL` (une requête d'état adressée à un réseau qui en propose une, toutes les 5 minutes tant que le transfert attend la contrepartie). |
| `status` | `QUEUED`, `SENDING`, `RETRYING`, `DELIVERED` ou `FAILED`. |
| `rail` | Le rail qui transporte le message ; vide tant que Didit n'en a pas choisi un pour un premier message. |
| `attempts` | Nombre de tentatives effectuées jusqu'ici. |
| `last_error` | Raison de l'échec de la dernière tentative, vide si aucune n'a échoué. Une courte raison qui indique l'étape et le type d'échec, par exemple `sygna permission request failed: HTTP 503` ou `sygna status query failed: no answer in time` ; lorsque GTR répond par une erreur, son propre code de statut et son message sont inclus. Les corps de réponse ne sont jamais inclus. |
| `next_attempt_at` | Moment où la prochaine tentative est effectuée, tant que le message est `QUEUED` ou `RETRYING`. |
| `delivered_at` | Moment où l'autre partie a pris en charge le message. |

Un échec dont le réseau peut encore se remettre (pas de réponse à temps, échec de connexion, HTTP `408`, `429` ou `5xx`) est réessayé après 30 secondes, 2 minutes, 10 minutes, 30 minutes puis 1 heure.
Lorsqu'un premier message échoue définitivement (le réseau l'a refusé, ou la sixième tentative a également échoué), le transfert passe à `COUNTERPARTY_VASP_NOT_REACHABLE` avec `last_error` dans sa `timeline`, et vous pouvez le réessayer avec `action: "resend"`.
Tout autre message qui échoue définitivement est consigné dans la `timeline`.
`delivery` ne remplace jamais le `status` du transfert : lisez l'échange à partir de `status`, et utilisez `delivery` pour voir pourquoi un transfert attend encore.

### Valeurs de statut héritées

Si vous envoyiez déjà `travel_rule_details.status` avant cette version, ces valeurs fonctionnent toujours et sont converties automatiquement.
Les variantes en camelCase de n'importe quel statut (par exemple `counterpartyVaspNotFound`) sont également acceptées, à la soumission comme dans les valeurs de condition d'une [règle](https://docs.didit.me/fr/transaction-monitoring/rules.md), et sont normalisées vers la forme canonique :

| Valeur héritée | Correspondance |
| - | - |
| `UNKNOWN` | `NOT_APPLICABLE` |
| `COMPLIANT` | `COMPLETED` |
| `PENDING_ACTION` | `ON_HOLD` |
| `PENDING_COUNTERPARTY` | `AWAITING_COUNTERPARTY` |
| `FAILED` | `CANCELLED` |
| `EXEMPT` | `NOT_APPLICABLE` |

## Configuration

Configurez votre profil VASP et votre politique de négociation une seule fois par application avec `GET`/`PUT /v3/travel-rule/settings/`.

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

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

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

`PUT` est une mise à jour partielle : n'envoyez que les champs que vous souhaitez modifier.

| Champ | Type | Description |
| - | - | - |
| `is_enabled` | boolean | Active le flux de négociation géré pour les transactions `travelRule` sortantes de cette application. Exige un `legal_name` contenant autre chose que des espaces (déjà enregistré ou inclus dans la même requête), faute de quoi la requête renvoie `400`. |
| `legal_name` | string | Dénomination légale de votre VASP. Affichée à la contrepartie sur la page de récupération par e-mail et renvoyée dans l'[annuaire des VASP](#annuaire). |
| `lei` | string | Votre identifiant d'entité juridique (LEI), renvoyé dans l'annuaire des VASP. |
| `jurisdiction` | string | Code de la juridiction d'exploitation de votre VASP, jusqu'à 8 caractères (par défaut `"EU"`). |
| `name_matching_strictness` | string | `NONE`, `STRICT`, `CONTAINS`, `DEFAULT` ou `FUZZY` - voir ci-dessous. |
| `confirmation_timeout_hours` | integer | Nombre d'heures pendant lesquelles un transfert `AWAITING_COUNTERPARTY` peut rester en attente avant d'expirer automatiquement. Minimum `1`, par défaut `48`. |
| `timeout_outcome` | string | Ce qui arrive à la transaction sous-jacente lorsqu'un transfert expire : `HOLD` la place en examen, `REJECT` la refuse, `PROCEED` laisse son statut inchangé. Par défaut `HOLD`. `sunrise_policy` et `non_responsive_policy` sont prioritaires lorsque l'une d'elles s'applique. |
| `sunrise_policy` | string | `HOLD`, `REJECT`, `PROCEED` ou `""` (par défaut) pour suivre `timeout_outcome` : ce qui arrive à un transfert expiré lorsque sa contrepartie est une entrée de l'annuaire située dans une juridiction listée dans `sunrise_jurisdictions`. Voir [Politiques de période de transition et de non-réactivité](#politiques-de-période-de-transition-et-d’absence-de-réponse). |
| `non_responsive_policy` | string | `HOLD`, `REJECT`, `PROCEED` ou `""` (par défaut) pour suivre `timeout_outcome` : ce qui arrive à un transfert expiré lorsque sa contrepartie est une entrée de l'annuaire dont la [réactivité](#réactivité) est `UNRESPONSIVE`. `sunrise_policy` s'applique en premier lorsque les deux correspondent. |
| `sunrise_jurisdictions` | string\[] | Lecture seule. Les pays que Didit considère en période de transition Travel Rule, actuellement `["CO", "KH", "VN"]`. Toute autre juridiction suit `timeout_outcome`. |
| `compliance_email` | string | Adresse e-mail du contact conformité de votre profil VASP. Le [réseau Didit](#le-réseau-didit) ne référence votre profil qu'une fois cette adresse confirmée via un lien envoyé par e-mail ; sa modification nécessite une nouvelle confirmation. |
| `is_discoverable` | boolean | Indique si votre profil VASP peut figurer dans le [réseau Didit](#le-réseau-didit) : trouvé dans `GET /v3/travel-rule/vasps/` par d'autres clients Didit et joint via le rail INTERNAL. La publication est soumise à d'autres conditions ; `didit_network` dans les paramètres indique celles qui manquent. Par défaut `true`. |
| `inbound_auto_accept` | boolean | Si `true`, une demande `TRP` entrante dont le portefeuille bénéficiaire est une entrée vérifiée de votre carnet d'adresses et dont le nom correspond est approuvée automatiquement. Si `false`, elle attend en `ON_HOLD` votre décision. Les autres chemins entrants ne lisent pas ce paramètre. Par défaut `true`. |
| `allow_self_declaration` | boolean | Si `true`, le [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) propose l'auto-déclaration comme méthode de preuve, et vous pouvez créer des entrées du [carnet d'adresses de portefeuilles](#carnet-d’adresses-de-portefeuilles) avec `self_declared: true`. Par défaut `true`. |
| `allow_screenshot_proof` | boolean | Si `true`, le [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) propose le téléversement d'une capture d'écran comme méthode de preuve de propriété. Une capture d'écran ne vérifie jamais à elle seule le portefeuille : votre équipe conformité l'examine et confirme le transfert. Moins fiable que la signature cryptographique : désactivez-la lorsque votre politique exige une preuve de niveau signature. Par défaut `true`. |
| `auto_wallet_verification` | boolean | Si `true`, un transfert sortant nécessitant une preuve de contrôle du portefeuille par l'utilisateur final crée automatiquement une session du [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md), liée à la transaction, et l'affiche dans le bloc `action_required` de la transaction ; les SDK Didit la lancent automatiquement lors de la [soumission côté client](https://docs.didit.me/fr/transaction-monitoring/sdk-transaction-submission.md). Par défaut `true`. |
| `counterparty_screening_enabled` | boolean | Filtrage optionnel des VASP contreparties pour les sanctions et la presse négative, [facturé par filtrage](#filtrage-de-la-contrepartie) au tarif Filtrage AML pour les entreprises. Si `true`, Didit filtre la contrepartie à laquelle un échange sortant aboutit au démarrage de celui-ci, en réutilisant un filtrage de moins de 30 jours. Par défaut `false` : rien n'est filtré ni facturé. |
| `vasp_attribution_enabled` | boolean | Si `true`, un transfert sortant qu'aucun rail ne peut acheminer est attribué via l'[analyse blockchain](#attribution-par-analyse-de-la-blockchain) : le portefeuille de destination est rattaché à son VASP propriétaire, l'entité est ajoutée à l'annuaire et les rails sont relancés une fois avec le nom attribué. Par défaut `true`. |
| `threshold_amount` | string (decimal) | Montant minimal de transfert déclenchant un échange géré, dans la devise par défaut de votre application : Didit le compare au montant du transfert converti dans cette devise (ou au `amount_in_default_currency` que vous envoyez), et non au montant dans la devise propre du transfert. Les transferts inférieurs passent directement à `NOT_APPLICABLE` et ne sont jamais négociés. Si Didit ne peut pas convertir le montant, le seuil n'est pas appliqué et le transfert est échangé. Les applications sandbox ne convertissent pas les montants : le seuil ne s'applique donc que si vous envoyez `amount_in_default_currency`. Par défaut `0.00`, conformément au cadre du règlement TFR de l'UE, qui ne prévoit aucun montant minimal : chaque transfert est donc vérifié quelle que soit sa taille. |
| `ownership_validation_mode` | string | `FULL`, `SIMPLIFIED` ou `AUTOMATED` : le niveau de preuve exigé pour la propriété d'un portefeuille. Voir [Modes de validation de propriété](#modes-de-validation-de-la-propriété). Par défaut `AUTOMATED`. |
| `simplified_validation_threshold` | string (decimal) | En mode `SIMPLIFIED`, un transfert inférieur à ce montant accepte une auto-déclaration. Par défaut `0.00`. |
| `data_sharing_fields` | object | Les champs que vous envoyez concernant vos propres utilisateurs, par type d'entité. Voir [Partage de données](#partage-de-données). |
| `matching_rules` | object | Le degré de rigueur avec lequel chaque champ des données de la contrepartie est comparé, par type d'entité. Voir [Correspondance par champ](#correspondance-par-champ). |
| `disabled_networks` | string\[] | Rails de réseau (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`) que vos transferts ne doivent PAS emprunter via l'adhésion de plateforme de Didit. Un réseau pour lequel vous avez connecté votre propre adhésion (Marketplace de la console) n'est jamais affecté par cette liste. Didit ne détient encore aucune adhésion de plateforme : la liste n'a donc aucun effet de routage aujourd'hui (un rail listé affiche `mode: off`). Par défaut `[]`. |
| `networks` | object | Lecture seule. Statut par réseau, indexé par `gtr` / `trust` / `verifyvasp` / `sygna` : `{rail, mode, platform_available, connected}` où `mode` vaut `own` (votre adhésion connectée), `didit` (l'adhésion de plateforme de Didit, pas encore disponible sur aucun réseau), `off` (désactivé par vous) ou `unavailable`. |
| `travel_address` | string | Lecture seule. Travel Address propre à votre VASP : le point de terminaison `TRP` d'interrogation entrante encodé, que les autres VASP utilisent pour vous joindre. Renseigné une fois la Travel Rule activée, par exemple `ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeS5uNDp3dGW3bweBWLKws442chRsyNxmmJnyWc7frxjjSeABVsYR11UuBePkw6E`. |

### Rigueur de la correspondance des noms

| Valeur | Comportement |
| - | - |
| `NONE` | Tout nom de bénéficiaire est accepté : aucune comparaison n'est effectuée. |
| `STRICT` | Les ensembles de tokens normalisés des deux noms doivent être strictement identiques. |
| `CONTAINS` | Tous les mots d'un nom figurent dans l'autre, quel que soit leur nombre. |
| `DEFAULT` | Chaque token du nom le plus court doit figurer dans le nom le plus long (correspondance par sous-ensemble), et le nom le plus court doit comporter au moins deux tokens, sauf si les deux noms sont identiques. |
| `FUZZY` | Un ratio de similarité entre les chaînes normalisées complètes doit être au moins égal à `0.85`. |

### Modes de validation de la propriété

`ownership_validation_mode` détermine le niveau de preuve exigé pour qu'un transfert puisse s'appuyer sur la propriété d'un portefeuille :

| Mode | Comportement |
| - | - |
| `FULL` | Une preuve est toujours requise : un message signé, un dépôt test ou une capture d'écran examinée. La parole de l'utilisateur final ne compte jamais. Le widget ne propose pas `SELF_DECLARATION`, son initiation renvoie `403`, et une entrée du carnet d'adresses créée avec `self_declared: true` renvoie `400`. |
| `SIMPLIFIED` | Une auto-déclaration est acceptée tant que le transfert est inférieur à `simplified_validation_threshold` ; au-delà, une preuve est requise. Un transfert entrant (une demande TRP, un message réseau ou un dépôt que vous signalez) est évalué selon son montant converti dans la devise par défaut de votre application, au taux en vigueur. Un actif sans taux, ainsi que chaque transfert d'une application sandbox, est considéré comme supérieur au seuil. |
| `AUTOMATED` | Le carnet d'adresses et les preuves précédentes de l'utilisateur final suffisent à eux seuls, y compris les auto-déclarations tant que `allow_self_declaration` vaut `true`. C'est la valeur par défaut, et le comportement de toutes les applications avant l'existence des modes. |

Un portefeuille est mémorisé pour chaque utilisateur final une fois vérifié.
En mode `FULL` et `SIMPLIFIED`, un transfert ultérieur ne le réutilise que si une preuve acceptée par le mode pour ce transfert le corrobore.
Lorsque `inbound_auto_accept` ne trouve aucun portefeuille correspondant dans le carnet d'adresses, un portefeuille crypto que vous avez enregistré sur les transactions d'un seul utilisateur final identifie le titulaire.
Didit ajoute ce portefeuille au carnet d'adresses de l'utilisateur final comme votre auto-déclaration, vérifié sauf si le mode est `FULL`. Il n'ajoute jamais un portefeuille que le carnet d'adresses référence déjà sous un autre nom de la même chaîne (`eth` et `ethereum`), y compris s'il a été enregistré comme appartenant à un autre VASP.
Les vérifications d'adresses que d'autres VASP effectuent avant d'envoyer (via `GTR`, `Sygna` et `VerifyVASP`) suivent elles aussi le mode : en `FULL`, un portefeuille que l'utilisateur final a seulement déclaré n'est pas signalé comme vous appartenant.

### Partage de données

`data_sharing_fields` choisit les champs que Didit envoie à la contrepartie au sujet de vos propres utilisateurs, séparément pour les personnes physiques et les entreprises :

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

* Les personnes physiques peuvent partager `name`, `wallet_address`, `address`, `national_id`, `customer_identification`, `date_of_birth` et `place_of_birth`. Les entreprises utilisent les cinq premiers.
* `name` et `wallet_address` sont toujours envoyés.
* Un type d'entité que vous n'avez jamais choisi partage tous les champs. Une liste vide est elle aussi un choix : elle ne partage que le nom et le portefeuille.
* Un `PUT` fusionne par type d'entité : l'envoi de `individual` laisse le choix enregistré pour `company` inchangé.
* Un choix qui ne peut pas satisfaire les obligations applicables au donneur d'ordre dans le régime de votre `jurisdiction` (règlement UE sur les transferts de fonds ou Recommandation 16 du GAFI), y compris une liste vide, est refusé avec `400`. Les erreurs figurent sous `data_sharing_fields`, indexées par type d'entité.

### Correspondance par champ

`matching_rules` définit le degré de rigueur avec lequel chaque champ des données de la contrepartie est comparé à celles que vous détenez :

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

Chaque valeur est `NONE`, `STRICT`, `CONTAINS`, `DEFAULT` ou `FUZZY`.
Valeurs par défaut : `name` suit `name_matching_strictness`, `date_of_birth` et `address` valent `NONE`, et `account` vaut `STRICT`.
Un champ omis conserve sa valeur par défaut, et un champ que le type d'entité ne compare pas (une entreprise n'a pas de `date_of_birth`) est refusé avec `400`.

### Seuils par juridiction

`threshold_amount` existe parce que le périmètre de la Travel Rule varie selon le régulateur.
Didit applique par défaut `0.00`, la position de l'UE : chaque transfert est donc échangé, sauf si vous relevez ce seuil délibérément :

| Régime | Seuil | Remarques |
| - | - | - |
| **UE** - Règlement sur les transferts de fonds (UE) 2023/1113 | **Aucun minimum** | Chaque transfert crypto entre CASP est couvert. La propriété d'un portefeuille auto-hébergé doit être vérifiée pour les transferts supérieurs à 1 000 EUR. |
| **Recommandation 16 du GAFI** (référence mondiale) | 1 000 USD/EUR | En dessous de ce seuil, le GAFI autorise un jeu de données réduit (noms et adresses de portefeuille, sans vérification). |
| **États-Unis** - BSA Travel Rule | 3 000 USD | Le seuil historique de FinCEN pour les transferts de fonds, appliqué aux monnaies virtuelles convertibles. |
| **Royaume-Uni** | Équivalent de 1 000 EUR | Informations complètes au-dessus du seuil ; jeu de données réduit en dessous (MLRs Part 7A). |
| **Singapour** - MAS PSN02 | 1 500 SGD | Exigences réduites en dessous du seuil. |
| **Suisse** - FINMA | Aucun minimum | Informations requises pour tous les transferts ; preuve de propriété attendue pour les portefeuilles auto-hébergés. |

Définissez `threshold_amount` au montant fixé par votre régulateur : les transferts inférieurs à ce montant sont résolus en `NOT_APPLICABLE` sans échange de données d'identité.
Les seuils évoluent : confirmez le montant en vigueur avec votre conseil juridique en conformité, plutôt que de considérer ce tableau comme un avis juridique.

### Modèle de données IVMS 101

Le standard du secteur pour les données échangées dans le cadre de la Travel Rule est **IVMS 101** (interVASP Messaging Standard).
`travel_rule_details.originator_data` et `travel_rule_details.beneficiary_data` sont des charges utiles de personne au format IVMS 101 : Didit les encapsule dans l'enveloppe native de chaque canal. Le canal TRP échange des blocs `IVMS101` (`ivms101.2023`, indiqué dans le champ `ivms_version` du transfert), le canal Sygna sa variante IVMS chiffrée en ECIES, et les canaux `GTR`, `TRUST` et `VERIFYVASP` leurs propres enveloppes chiffrées. Une seule structure de charge utile atteint ainsi chaque contrepartie dans le format attendu par son protocole.

Didit lit ces clés dans chaque charge utile de personne lorsqu'il construit le message IVMS 101 : `first_name` et `last_name` (ou un seul `name` ou `full_name`), `date_of_birth`, `place_of_birth`, `residence_country` (ou `country`), `vendor_data` (ou `customer_identification`) et `wallet_address`.

Deux règles pratiques :

* **Écriture latine.** IVMS 101 exige des valeurs en système d'écriture latin ; translittérez les noms qui sont écrits nativement dans un autre système d'écriture. La [correspondance des noms](#rigueur-de-la-correspondance-des-noms) de Didit compare des ensembles de jetons normalisés : les différences d'ordre et de casse ne font donc pas échouer un échange, mais des systèmes d'écriture différents des deux côtés le feront.
* **Envoyez les données que vous avez vérifiées.** Lorsque la personne concernée par `vendor_data` est un utilisateur disposant d'une vérification KYC Didit approuvée, Didit transmet l'identité vérifiée, et non les valeurs que vous avez saisies : voir [Données vérifiées et provenance](#données-vérifiées-et-provenance).
  Pour toute autre personne, Didit transmet exactement le `originator_data` que vous envoyez. Construisez-le donc à partir de l'identité que vous avez vérifiée lors de l'onboarding, et non à partir d'un champ de formulaire non vérifié. C'est ce qu'exige la formulation de la FATF : « informations **requises et exactes** sur le donneur d'ordre ».
  Sur le canal `INTERNAL`, l'application du bénéficiaire reçoit aussi le `subject` que vous avez envoyé (nom, `vendor_data`, date de naissance, pays et adresse) en tant que contrepartie de sa transaction entrante.

#### Données vérifiées et provenance

Lorsque la personne concernée par un transfert est l'un de vos utilisateurs, une personne physique disposant d'une vérification KYC approuvée, Didit construit ce côté de l'échange à partir des données vérifiées :

* le donneur d'ordre d'un transfert sortant que vous créez avec `POST /v3/transactions/` ;
* le bénéficiaire d'un transfert entrant que vous signalez avec `POST /v3/transactions/`, ou avec `POST /v3/travel-rule/inbound/` lorsque le `holder_vendor_data` du portefeuille correspondant désigne cet utilisateur.

Sur `POST /v3/transactions/`, les deux cas s'appliquent à une transaction dont `transaction_category` vaut `travel_rule` pendant que la Travel Rule est activée.

Les noms vérifiés, la date et le lieu de naissance, le numéro et le type de document, l'adresse et le pays de résidence (déduit de cette adresse) prévalent sur les valeurs que vous avez envoyées. Un champ que la vérification ne contient pas conserve votre valeur, de même qu'une adresse complète que vous avez envoyée lorsque l'adresse vérifiée n'est pas assez complète pour être transmise.

Les `metadata` du transfert, dans l'objet `travel_rule` intégré à la transaction, indiquent d'où provient chaque champ et où vos valeurs différaient :

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

Les clés `*_provenance` associent chaque champ à `verified` ou `caller`. Les clés `*_verified_differences` listent chaque champ pour lequel vous avez envoyé une valeur différente de la valeur vérifiée, sans tenir compte de la casse, des espaces ni des codes pays à deux ou trois lettres. Lorsque les données d'un transfert entrant sont [masquées](#données-de-la-contrepartie-masquées), les différences sont omises. Didit calcule ces deux types de clés : les copies de ces clés, ou de `name_match`, que vous envoyez dans `travel_rule_details.metadata` sont donc supprimées.

#### Envoyer directement de l'IVMS 101

En plus des objets à plat `originator_data` et `beneficiary_data`, `travel_rule_details` (sur `POST /v3/transactions/`) et `POST /v3/travel-rule/inbound/` acceptent des données IVMS 101 authentiques :

* un bloc `originator` et un bloc `beneficiary`, avec `originatorPersons` ou `beneficiaryPersons` contenant un `naturalPerson` ou un `legalPerson`, ainsi que `accountNumber` ;
* les mêmes blocs à l'intérieur d'un objet `ivms101` (ou `IVMS101`) ;
* une personne IVMS 101, ou un bloc complet, en tant que `originator_data` ou `beneficiary_data`.

Chaque côté accepte une seule personne : un `naturalPerson`, ou un `legalPerson`, ce qui fait de la partie une entreprise. Un `originator_data` ou `beneficiary_data` à plat et non vide l'emporte sur un bloc pour le même côté.

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

Un bloc mal formé renvoie une erreur `400`, avec une clé correspondant à son chemin, par exemple `{"travel_rule_details": {"originator.originatorPersons": ["must be a non-empty list of originator persons."]}}`. Sur `POST /v3/travel-rule/inbound/`, les mêmes clés se trouvent au niveau supérieur, sous `ivms101.` lorsque vous avez utilisé l'enveloppe.

#### Données requises par régime

Pendant que la Travel Rule est activée, un transfert sortant égal ou supérieur à votre seuil doit comporter ce qu'exige son régime, faute de quoi il répond `400`. Seuls les côtés que vous avez envoyés sont vérifiés, les données vérifiées comptent pour le donneur d'ordre, et les erreurs apparaissent sous `travel_rule_details.originator_data` ou `travel_rule_details.beneficiary_data`, par exemple `"EU TFR requires the originator name."` ou `"FATF Recommendation 16 requires the beneficiary wallet address."`

| Régime | Donneur d'ordre | Bénéficiaire |
| - | - | - |
| EU TFR (votre `jurisdiction` est un État membre de l'UE, l'Islande, le Liechtenstein, la Norvège, `EU` ou `EEA` ; `EU` est la valeur par défaut) | Le nom, plus l'adresse, le numéro de document officiel et le numéro d'identification client ensemble, ou la date et le lieu de naissance. Une entreprise n'a pas d'alternative liée à la naissance. | Nom et adresse |
| FATF Recommendation 16 (toute autre juridiction, y compris une juridiction vide) | Le nom, plus l'un des éléments suivants : l'adresse, le numéro de document officiel ou le numéro d'identification client, ou la date et le lieu de naissance ensemble. Une entreprise n'a pas d'alternative liée à la naissance. | Nom et adresse |

Pour le donneur d'ordre, la vérification lit les champs que votre application partage dans [`data_sharing_fields`](#partage-de-données), car ce sont les seuls qu'un canal transmet. Le bénéficiaire est vérifié tel que vous l'avez envoyé. Lorsque le transfert ne satisfait le régime qu'avec un champ que vous ne partagez pas, le `400` nomme l'exigence du régime, puis les champs que vos paramètres partagent.

Le numéro de document officiel est `national_id` (ou `document_number`), le numéro d'identification client est `customer_identification` (ou `vendor_data`), et la date de naissance est `date_of_birth`. Un `beneficiary_data.wallet_address` vide est rempli à partir de `counterparty.payment_method.account_id` avant la vérification. Un retrait UTXO dont la sortie bénéficiaire a été déduite par Didit à partir de `travel_rule_details.utxo` est considéré comme la comportant. Un transfert Lightning auquel la Travel Rule ne s'applique pas n'est pas vérifié.

### Travel Addresses

Une **Travel Address** est une chaîne compacte et encodée qui pointe vers l'endpoint `TRP` d'entrée d'un VASP ; elle n'expose jamais d'URL brute ni d'adresse de portefeuille.
Une fois Travel Rule activé, Didit génère votre propre `travel_address` (renvoyée en lecture seule dans les paramètres) afin que vous puissiez la partager avec les autres VASP qui vous joignent via le canal `TRP`.
Chacun de vos portefeuilles du carnet d'adresses reçoit également un `receiving_travel_address` propre : une requête envoyée à cette adresse désigne ce portefeuille, de sorte que Didit n'a pas besoin de le retrouver grâce au numéro de compte ou au nom du titulaire.
Le serveur TRP public de Didit ne répond pas encore aux autres VASP ; la réception via `TRP` n'est donc pas disponible tant que ce n'est pas le cas : voir [Réseaux Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md).
Lorsque vous connaissez la Travel Address d'une contrepartie, enregistrez-la sur l'[entrée du carnet d'adresses de portefeuille](#carnet-d’adresses-de-portefeuilles) du portefeuille de destination afin que Didit achemine directement les transferts de ce bénéficiaire via `TRP`, au lieu de recourir à l'e-mail.
Didit valide les Travel Address que vous enregistrez sur une entrée du carnet d'adresses de portefeuille ou envoyez dans `originating_vasp` : chacune doit commencer par `ta` et se décoder en une URL de requête contenant `t=i`, si bien qu'un espace réservé tel que `ta...` renvoie `400`.
Un `beneficiary_data.travel_address` sur une transaction n'est pas validé : si elle ne se décode pas, le canal `TRP` est ignoré.

L'encodage est un Base58Check avec le préfixe `ta` : l'hôte et le chemin de l'endpoint (qui porte toujours la paire de requête `t=i`), suivis d'une somme de contrôle de quatre octets issue d'un double SHA-256, le tout encodé en base58. Une adresse réelle ressemble donc à ceci et atteint environ 100 caractères :

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

Lorsque Didit valide une Travel Address, toute valeur qui n'est pas une adresse bien formée dont la somme de contrôle est valide renvoie `400` avec `"Invalid travel address."`.
Prenez une Travel Address réelle dans la réponse renvoyée par vos paramètres, ou auprès de la contrepartie elle-même.

## Tester sans identifiants réseau

Vous n'avez pas besoin d'une adhésion à GTR, TRUST, VerifyVASP ou Sygna pour tester les parcours qui ne nécessitent pas d'adhésion. Vous avez tout de même besoin d'une clé API Didit pour l'application testée.

* **Sandbox sortant :** soumettez une transaction `travelRule` depuis une application sandbox. Après les contrôles de seuil et de portefeuille auto-hébergé, le [Didit Test VASP](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) y répond au sein de Didit avec le scénario que vous choisissez selon le portefeuille ou le nom du bénéficiaire : acceptation, refus, nom non concordant, injoignable, expiration du délai ou auto-hébergé. Aucun réseau, aucun e-mail ni aucun autre client de Didit ne reçoit quoi que ce soit.
* **Sandbox entrant :** faites envoyer un transfert par le test VASP avec [Trigger Test VASP Inbound Transfer](https://docs.didit.me/fr/management-api/travel-rule/trigger-test-vasp-inbound.md), ou enregistrez-en un vous-même avec [Register Inbound Travel Rule Transfer](https://docs.didit.me/fr/management-api/travel-rule/register-inbound.md). Testez un portefeuille inconnu, un portefeuille non vérifié, une non-concordance de nom de bénéficiaire et un portefeuille vérifié correspondant. Ces scénarios permettent de tester de véritables décisions entrantes sans contacter de réseau.
* **Deux applications en production contrôlées :** utilisez le canal `INTERNAL` entre deux de vos applications en production (l'application destinataire est détectable et prouve la maîtrise du portefeuille de destination au moyen d'un message signé ou d'un dépôt de test), ou une contrepartie TRP que vous contrôlez, pour tester un échange entre deux parties sans identifiants commerciaux de réseau. Les applications en production peuvent entraîner des frais et des notifications normaux ; utilisez des identités de test et des contreparties que vous contrôlez.

Voir [Tester Travel Rule en sandbox](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) pour chaque scénario et son résultat exact. Les tests sandbox ou de protocole local réussis ne valident pas les identifiants, l'adhésion ni l'interopérabilité en production d'un réseau commercial. Ceux-ci nécessitent un sandbox de réseau autorisé ou un échange contrôlé en production avec des identifiants d'adhésion valides.

## Envoyer un transfert

Incluez le transfert dans [`POST /v3/transactions/`](https://docs.didit.me/fr/transaction-monitoring/transactions) en définissant `transaction_category` sur `travelRule` (ou `travel_rule`) et en joignant un objet `travel_rule_details` (alias : `travelRule`).

Le bloc `travel_rule_details` est facultatif, mais dès que vous le joignez, `travel_rule_details.status` est obligatoire : un bloc sans ce champ renvoie `400`.
Lorsque Travel Rule est **activé** pour votre application, Didit ignore la valeur `status` que vous envoyez et pilote lui-même l'échange :

1. Si le montant est inférieur au `threshold_amount` configuré, le transfert passe immédiatement à `NOT_APPLICABLE` et aucune négociation n'a lieu.
2. Si le moyen de paiement de la contrepartie déclare la destination comme portefeuille auto-hébergé (`payment_method.method_type: "unhosted_wallet"`), aucun canal n'est essayé : le transfert passe directement à `UNCONFIRMED_OWNERSHIP` et le widget de propriété du portefeuille est généré automatiquement (voir [Portefeuilles auto-hébergés déclarés](#portefeuilles-auto-hébergés-déclarés)).
3. Sinon, les canaux sont essayés dans l'ordre de priorité, et le premier capable d'atteindre la contrepartie prend en charge l'échange :
   * `INTERNAL` : exactement un autre client Didit détient une preuve par message signé ou par dépôt test pour `beneficiary_data.wallet_address`, établie après le dernier changement de titulaire du portefeuille. Votre propre application ne compte jamais, et lorsque votre utilisateur final a choisi un membre du réseau Didit dans le [widget de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md), seule la preuve de ce membre compte. L'application de ce client est en production (et non en sandbox), a Travel Rule activé et `is_discoverable` défini, et correspond au VASP que vous avez indiqué lors de l'envoi de `counterparty_vasp_lei` ou `counterparty_vasp_name`. Un portefeuille seulement déclaré, prouvé par une capture d'écran, non vérifié ou simplement répertorié dans un carnet d'adresses ne reçoit jamais rien par `INTERNAL`.
   * `TRP` : l'actif du transfert dispose d'une correspondance Digital Token Identifier pour le réseau du transfert (voir [Identifiants d'actifs TRP](#identifiants-d’actifs-trp)), et la charge utile contient `beneficiary_data.travel_address`, l'entrée du carnet d'adresses de la destination en contient une, ou la contrepartie se résout dans l'[annuaire](#annuaire) (par `counterparty_vasp_lei` ou `counterparty_vasp_name`) avec un point de terminaison TRP.
   * `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` : vous avez connecté votre propre adhésion à ce réseau dans **Marketplace**, et la contrepartie se résout en identifiant de réseau (champ explicite ou entrée de l'annuaire, voir [Champs de résolution de la contrepartie](#champs-de-résolution-de-la-contrepartie)).
     Didit ne détient aucune adhésion de plateforme sur ces réseaux pour l'instant (il est en cours d'onboarding en tant que prestataire), donc `disabled_networks` ne modifie pas cette étape aujourd'hui.
   * `EMAIL` : `beneficiary_data.counterparty_vasp_email` a été fourni, ou `counterparty_vasp_name` se résout en une entrée de l'annuaire avec un e-mail de conformité.
4. Si aucun canal ne parvient à résoudre une contrepartie, Didit exécute la chaîne de [découverte de la contrepartie](#découverte-de-la-contrepartie). Lorsque `vasp_attribution_enabled` est activé, cette chaîne comprend l'[attribution par analyse de blockchain](#attribution-par-analyse-de-la-blockchain) : le portefeuille de destination est rattaché à son VASP propriétaire, le nom attribué est inscrit dans `beneficiary_data.counterparty_vasp_name` (si le champ est vide) et dans l'[annuaire](#annuaire), puis les canaux sont relancés une fois. Ainsi, un portefeuille attribué à un VASP dont l'entrée de l'annuaire comporte un point de terminaison TRP, un identifiant de réseau ou un e-mail de conformité est acheminé lors de ce second passage.
5. Si la contrepartie reste non résolue, le transfert passe à `COUNTERPARTY_VASP_NOT_FOUND`. Pour les transferts sortants, Didit génère automatiquement le [widget de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) (lorsque `auto_wallet_verification` est activé) afin que le transfert puisse tout de même aboutir comme transfert vers un portefeuille auto-hébergé, et vous pouvez relancer le routage plus tard avec `action: "resend"` une fois que vous disposez de meilleures données sur la contrepartie.

Les étapes 1 et 2 s'exécutent pendant la requête. Les étapes 3 à 5 s'exécutent juste après la réponse : le transfert revient avec le statut `AWAITING_COUNTERPARTY` et `protocol: null`, et chaque résultat arrive sous forme de webhook [`travel_rule.status.updated`](#webhooks).

### Identifiants d'actifs TRP

`TRP` désigne l'actif transféré par son Digital Token Identifier (DTI) ISO 24165.
Un DTI identifie un jeton sur une seule blockchain, donc un jeton multi-chaînes possède un DTI par réseau.

Didit envoie les transferts via `TRP` pour ces actifs :

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

Les identifiants `USDC` sont ceux que l'émetteur du jeton publie dans son livre blanc MiCA.

Pour un jeton multi-chaînes, le transfert doit indiquer son réseau dans `transaction_details.crypto_params.crypto_chain` (par exemple `CELO`, `BASE`, `OP`, `ARB` ou `POL`).
Sans réseau, ou sur un réseau sans correspondance, le transfert n'est pas envoyé via `TRP` et Didit essaie les canaux restants : d'abord les réseaux d'interopérabilité, puis l'e-mail.

`USDT` ne dispose pas encore de correspondance.
Un jeton sans correspondance DTI, comme le stablecoin propre à une entreprise, nécessite que son émetteur enregistre un DTI auprès de l'autorité d'enregistrement ISO 24165, un par jeton et par blockchain.
Une fois les DTI créés, contactez le support Didit avec chaque DTI, sa blockchain et les décimales du jeton, et Didit ajoute la correspondance.

Les demandes entrantes `TRP` qui comportent un DTI par chaîne correspondant sont enregistrées avec la devise du jeton, le montant exprimé dans les décimales du jeton, et le réseau.

### Découverte de la contrepartie

Un transfert qui ne comporte qu'une adresse de portefeuille atteint tout de même sa contrepartie dès que quelqu'un peut l'identifier.
Avant qu'un transfert sortant soit déclaré non routable, Didit vérifie, dans cet ordre, et s'arrête à la première étape qui désigne un VASP disposant d'un itinéraire :

| Étape | Ce qu'elle fait |
| - | - |
| `internal_address_book` | Un autre client Didit a prouvé le contrôle du portefeuille (une signature ou un dépôt test), donc le canal `INTERNAL` lui transmet le transfert. Une entrée de carnet d'adresses non prouvée ne compte jamais. |
| `wallet_attribution` | L'attribution du portefeuille par analyse de blockchain, rapprochée d'un VASP de l'annuaire disposant d'un itinéraire. Un résultat déjà enregistré est réutilisé et facturé une seule fois. Nécessite `vasp_attribution_enabled`. |
| `network_lookup` | Un réseau Travel Rule qui indique « qui détient cette adresse », lorsque votre application y a accès. Seul GTR propose cette fonctionnalité aujourd'hui, et elle s'exécute lorsque le canal GTR envoie. Sinon, l'étape est enregistrée comme `skipped` avec la raison. |
| `customer_hint` | Le nom du VASP, le LEI ou la juridiction que vous avez transmis dans `beneficiary_data`, mis en correspondance avec un VASP de l'annuaire disposant d'une route. Un `travel_address` ou un e-mail de conformité est acheminé par son propre canal. |
| `end_user` | Personne n'a désigné la contrepartie, donc l'utilisateur final est invité à la choisir. Voir ci-dessous. |

Le champ `discovery` du transfert liste chaque étape exécutée avec son `outcome` (`match`, `miss` ou `skipped`), une `reason` lisible par machine, le `vasp` et le `rail` trouvés, ainsi qu'un horodatage.
Un VASP que vous ou l'utilisateur final avez choisi prime sur toute étape automatique : la découverte ne le remplace jamais par sa propre supposition.

Lorsqu'aucune étape ne désigne de contrepartie, le transfert passe à `COUNTERPARTY_VASP_NOT_FOUND`, ou à `COUNTERPARTY_VASP_NOT_REACHABLE` lorsqu'un VASP a été désigné mais qu'aucune de ses routes n'est disponible pour votre application.
Le transfert porte alors une `reason` (`awaiting_end_user`, `no_directory_match`, `directory_match_no_route`, ou `picked_member_without_proven_wallet` lorsque le membre choisi par l'utilisateur final n'a pas prouvé la propriété du portefeuille) ainsi qu'une liste de `next_actions` :

| `action` | Signification |
| - | - |
| `select_counterparty_vasp` | L'utilisateur final choisit le VASP de destination dans le [widget de portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md), ou vous le désignez vous-même avec `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}` sur [endpoint de transfert](#envoyer-un-transfert). Le transfert est acheminé vers ce VASP puis renvoyé. |
| `declare_self_hosted` | L'utilisateur final déclare le portefeuille auto-hébergé dans le même widget. |
| `add_vasp_hint` | Envoyez un nom de VASP, un LEI ou un `travel_address` dans `beneficiary_data`, puis renvoyez la requête. |
| `update_compliance_email` | Transmettez à nouveau l'e-mail de conformité de la contrepartie lorsque le premier a été refusé ; voir [Pourquoi un transfert est bloqué](#pourquoi-un-transfert-est-bloqué). |
| `resend` | Relancez la découverte avec ce qui a changé. |

Le lien du widget correspond à `url` dans les actions effectuées par l'utilisateur final.
Lorsqu'une action de vérification de propriété du portefeuille est en attente, le bloc `action_required` de la transaction (type `wallet_ownership`) porte la même `reason` et les mêmes `next_actions`, de sorte qu'un client qui suit déjà `action_required` affiche le sélecteur sans travail supplémentaire.

### Champs de résolution de la contrepartie

Tout ce que Didit sait de la contrepartie provient de `travel_rule_details.beneficiary_data`.
Ces champs pilotent la résolution des canaux ; tous sont facultatifs :

| Champ | Pilote |
| - | - |
| `wallet_address` | La correspondance dans le carnet d'adresses `INTERNAL`, la recherche dans le carnet d'adresses `TRP` et les flux de vérification de propriété du portefeuille. Si vous ne l'indiquez pas, Didit utilise `counterparty.payment_method.account_id`. |
| `chain` | Délimite les deux recherches dans le carnet d'adresses : une entrée ne correspond que si son `chain` est égal à cette valeur (insensible à la casse) ou si l'entrée n'a pas de `chain`. Si vous ne l'indiquez pas, Didit le renseigne à partir de `transaction_details.crypto_params.crypto_chain`, de `custom_properties.crypto_chain` ou de la devise du transfert, en majuscules, mais uniquement si cette valeur est une chaîne reconnue par Didit (par exemple `ETH`, `BTC`, `SOL`, `TRX`). Sinon, le transfert ne porte aucune chaîne et correspond aux entrées sur n'importe quelle chaîne. Pour un transfert `USDT` ou `USDC` sans chaîne explicite, Didit la déduit d'un hash de transaction dans `transaction_details.payment_reference_id` (un hash `0x` donne `ETH`). |
| `memo` | Le tag de destination ou le mémo de `wallet_address` sur un réseau à mémo. Il fait partie du portefeuille partout où `wallet_address` est comparée (voir [Réseaux à mémo et à tag](#réseaux-à-mémo-et-à-tag)). Alias : `destinationTag`. |
| `travel_address` | Route directement via `TRP`, le signal le plus fort que vous puissiez transmettre. |
| `counterparty_vasp_lei` | Recherche dans l'annuaire par LEI pour `TRP`, `GTR`, `TRUST`, `VERIFYVASP` et `SYGNA`. Vérifié aussi par rapport à l'identité que l'endpoint TRP renvoie : une divergence, ou une identité sans LEI, interrompt l'échange avec `COUNTERPARTY_VASP_NOT_REACHABLE`. |
| `counterparty_vasp_name` | Recherche dans l'annuaire pour `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` et `EMAIL` : le nom exact, le même nom écrit autrement (`B.V.` pour `BV`, un accent), puis un alias d'une entrée. Vérification approximative par rapport à l'identité TRP lorsque les deux sont présents. |
| `counterparty_vasp_jurisdiction` | Code pays ISO 3166 de l'entité réceptrice lorsque le VASP nommé est une marque regroupant plusieurs entités réglementées, ou lorsque plusieurs entrées de l'annuaire portent le même nom : voir [Marques et leurs entités](#marques-et-leurs-entités). |
| `counterparty_gtr_vasp_code` | Identifiant de réseau `GTR` explicite : contourne l'annuaire. |
| `counterparty_trust_membership_id` | Identifiant de réseau `TRUST` explicite : contourne l'annuaire. |
| `counterparty_verifyvasp_id` | Identifiant de réseau `VerifyVASP` explicite : contourne l'annuaire. |
| `counterparty_sygna_vasp_code` | Code VASP `Sygna Bridge` explicite : contourne l'annuaire. |
| `counterparty_vasp_email` | Contact de conformité explicite pour le canal `EMAIL` : contourne l'annuaire. |

### Formes de chaîne

Sur une chaîne de comptes (chaînes EVM, Solana, Tron), un transfert déplace des fonds d'une adresse vers une autre, et Didit lit les deux portefeuilles directement sur la transaction.
Trois autres formes nécessitent davantage, et ce que Didit a résolu pour elles figure dans le transfert sous `chain_details` (vide, `{}`, sur une chaîne de comptes).
`chain_details.originator` et `chain_details.beneficiary` portent chacun l'`address`, son `memo` et la `source` d'où il provient, et la mise en correspondance et l'acheminement utilisent d'abord les portefeuilles indiqués à cet endroit.

#### Transferts UTXO

Une transaction Bitcoin, Litecoin, Bitcoin Cash ou Dogecoin dépense des entrées provenant d'un nombre quelconque d'adresses et paie un nombre quelconque de sorties, dont certaines renvoient la monnaie à l'expéditeur.
Envoyez les entrées et sorties sous `travel_rule_details.utxo` (montants dans l'unité propre à la monnaie, sous forme de chaînes décimales), et Didit déduit ce que vous ne déclarez pas :

* **Donneur d'ordre** : `originator_data.wallet_address` lorsque vous l'envoyez, sinon l'adresse d'entrée ayant apporté le montant le plus élevé (`source: largest_input`).
* **Bénéficiaire** : le portefeuille déclaré, sinon l'unique sortie qui ne renvoie pas de monnaie à une adresse d'entrée (`only_non_change_output`), sinon la sortie correspondant au montant du transfert (`amount_match`).

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

Le transfert transporte alors :

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

`chain_details.warnings` signale les incohérences : `declared_originator_not_an_input`, `declared_beneficiary_not_an_output`, `beneficiary_output_ambiguous` (aucune sortie unique ne pouvait être distinguée) et `beneficiary_output_amount_differs`.
Un `utxo` sur toute autre chaîne renvoie une erreur `400`, et chaque liste contient au plus 1 000 entrées.
Un dépôt signalé reprend le même objet `utxo` (voir [Enregistrement sunrise après dépôt](#enregistrement-a-posteriori-sunrise)).

#### Réseaux à mémo et à tag

Sur XRP, Stellar (XLM), EOS, Cosmos (ATOM) et Hedera (HBAR), une plateforme d'échange reçoit tous ses clients sur une même adresse partagée et les distingue grâce au tag de destination ou au mémo.
Le mémo fait donc partie du portefeuille : envoyez-le comme `payment_method.memo` sur le participant, ou comme `memo` dans `originator_data` / `beneficiary_data` (alias `destinationTag`), et Didit compare l'adresse et le mémo ensemble.

* Une entrée du carnet d'adresses avec un mémo ne correspond qu'aux transferts qui mentionnent ce mémo. Une entrée sans mémo correspond à l'ensemble de l'adresse. Un transfert qui ne mentionne aucun mémo n'atteint jamais une entrée qui en a un : sur une adresse partagée, un dépôt sans son tag n'appartient à personne.
* Une X-address XRP contient son tag : Didit la découpe en adresse classique et en tag (`source: x_address`), si bien qu'un portefeuille enregistré sous l'une ou l'autre forme correspond à un transfert exprimé sous l'autre forme.
* Chaque mémo sur une adresse partagée constitue son propre moyen de paiement sur la transaction.
* Formats acceptés par réseau : un tag de destination XRP est un nombre entier de 0 à 4294967295 (un nombre JSON est accepté) ; un mémo Stellar comporte jusqu'à 28 octets de texte, un identifiant sur 64 bits ou un hash de 32 octets en hexadécimal ; EOS accepte jusqu'à 256 octets, Cosmos jusqu'à 256 caractères, Hedera jusqu'à 100 octets. Un mémo sur une chaîne qui n'en a pas (Bitcoin, chaînes EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin, Lightning) renvoie une erreur `400`.

#### Lightning

Un paiement Lightning n'a pas d'adresse de bénéficiaire on-chain : le payeur détient une demande de paiement.
Sur un transfert de crypto-monnaie sur Bitcoin ou Lightning, `payment_method.account_id` peut contenir cette demande, et Didit identifie le bénéficiaire à partir de celle-ci sans contacter aucun réseau :

| Ce que vous envoyez | Bénéficiaire |
| - | - |
| Facture BOLT11 (`lnbc...`) | Le nœud bénéficiaire, indiqué dans la facture ou retrouvé à partir de sa signature. |
| Offre ou facture BOLT12 (`lno1...`, `lni1...`) | Le nœud émetteur, sauf s'il se cache derrière des chemins masqués. |
| Lightning Address (`name@domain`) | L'adresse elle-même. |
| Identifiant de nœud | Le nœud lui-même. |
| LNURL (`lnurl1...`, `lnurlp://`) | Aucun : elle désigne un service, pas un bénéficiaire. |

Le bénéficiaire est enregistré comme compte et comme `chain_details.beneficiary` (`source: invoice_payee`), sur la chaîne `LIGHTNING`, et ce que dit la demande figure dans `chain_details.lightning`.
Lorsque la demande ne nomme aucun bénéficiaire, un transfert sortant est `NOT_APPLICABLE`, avec la raison indiquée dans `chain_details.not_applicable_reason` et dans sa `timeline` : `LIGHTNING_NO_PAYEE_IDENTIFIER`, `LIGHTNING_BOLT12_BLINDED_PAYEE` ou `LIGHTNING_LNURL_NAMES_NO_PAYEE`.
Une valeur n'est interprétée comme Lightning que sur bitcoin, sur `LIGHTNING`, ou lorsqu'aucune chaîne n'est indiquée (une Lightning Address uniquement sur bitcoin ou `LIGHTNING`) : sur toute autre chaîne, c'est l'adresse propre à cette chaîne.

> [!NOTE]
>
> Le canal `INTERNAL` n'atteint un autre client Didit que par le biais d'un portefeuille dont ce client a prouvé la maîtrise au moyen d'un message signé ou d'un dépôt test, et ces preuves existent aujourd'hui pour Bitcoin, les chaînes EVM, Solana et Tron.
> Sur les réseaux à mémo, Lightning, Litecoin, Bitcoin Cash et Dogecoin, un transfert n'atteint donc jamais un autre client Didit via `INTERNAL`.
> La correspondance par mémo, tag et Lightning s'applique néanmoins à votre propre carnet d'adresses : transferts entrants, dépôts signalés et demandes `TRP` adressées à vos portefeuilles.

### Mise à jour du portefeuille donneur d'ordre

Un retrait est souvent déclaré depuis le portefeuille principal de votre client, et émis depuis les adresses qui détenaient les fonds.
Enregistrez le portefeuille d'où les fonds sont réellement partis avec `PATCH /v3/transactions/{transaction_id}/travel-rule/` :

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

* Transferts sortants uniquement, et jamais un transfert `CANCELLED` ou `EXPIRED` (`409`).
* Lorsque les entrées UTXO du transfert sont connues, le portefeuille doit en faire partie (sinon `400`). Envoyez aussi `memo` sur un réseau à mémo.
* Seul l'enregistrement est modifié : rien n'est envoyé à nouveau à la contrepartie et le statut reste inchangé. `chain_details.originator` devient le nouveau portefeuille avec `source: updated`, la `timeline` enregistre l'ancien et le nouveau portefeuille, et la contrepartie `INTERNAL` du transfert est mise à jour de la même manière.

### Attribution par analyse de la blockchain

Lorsque vous ne transmettez qu'une adresse de portefeuille, Didit peut tout de même identifier qui se trouve de l'autre côté.
Si aucun canal ne peut acheminer le transfert, Didit compare le portefeuille de destination aux données d'attribution de l'analyse blockchain ; la même analyse que celle qui alimente le [filtrage des transactions crypto](https://docs.didit.me/fr/transaction-monitoring/aml-screening.md) sait quels portefeuilles appartiennent à quelles plateformes d'échange et quels dépositaires.
L'attribution ne s'exécute que sur les transferts sortants qui comportent à la fois un portefeuille de destination et un `chain` (voir [Champs de résolution de la contrepartie](#champs-de-résolution-de-la-contrepartie)) ; sans chaîne, elle est ignorée.

Lorsque le portefeuille est attribué à une entité connue, Didit :

1. Enregistre l'attribution sur le transfert : `beneficiary_data.counterparty_vasp_name` est renseigné (si vous ne l'avez pas fourni), et les métadonnées du transfert contiennent un bloc `vasp_attribution` avec le nom de l'entité, son type, son pays et `source: "blockchain_analytics"`.
2. Ajoute l'entité à l'[annuaire VASP](#annuaire) (ou rapproche l'entrée existante), afin que l'attribution reste stable d'un transfert à l'autre.
3. Relance une fois les canaux avec le nom attribué : une entrée d'annuaire qui comporte déjà un endpoint TRP permet d'acheminer immédiatement le transfert, au lieu de le faire aboutir à `COUNTERPARTY_VASP_NOT_FOUND`.

L'appel d'attribution tient lieu de filtrage crypto complet : la transaction reçoit l'ensemble des résultats de filtrage (score de risque, catégorie de risque du portefeuille, instantané du réseau), exactement comme si la surveillance crypto avait été effectuée, et `custom_values.aml_screening_trigger` de la transaction prend la valeur `travel_rule_attribution`, afin que vous puissiez voir que le filtrage a été exécuté parce que le routage Travel Rule l'exigeait.
Lorsque la surveillance crypto a déjà filtré le même portefeuille sur cette transaction, le résultat enregistré est réutilisé : pas de second appel d'analyse, pas de second débit.
L'attribution ne bloque jamais la création de la transaction : si la recherche d'analyse échoue ou si votre solde ne suffit pas à en couvrir le coût, le transfert passe simplement à `COUNTERPARTY_VASP_NOT_FOUND` et la tentative n'est pas facturée.
Avec `action: "resend"`, une attribution enregistrée précédemment est réutilisée au lieu d'être interrogée à nouveau.

Désactivez toute l'étape avec `vasp_attribution_enabled: false` dans les [paramètres](#configuration).
Consultez [Tarifs](#tarifs) pour savoir comment l'appel d'attribution est facturé.

Lorsque Travel Rule est **désactivé**, `travel_rule_details` (y compris un `status` fourni par l'appelant, ancien ou actuel) est tout de même enregistré sur la transaction, mais de façon passive : aucun rail n'est déclenché.
Utilisez cette option pour compléter rétroactivement les données de conformité que vous suiviez déjà avec votre propre processus.
Un transfert **sortant** passif n'ouvre aucun échange : il est donc facturé comme Filtrage des transactions, et non sur la [ligne Travel Rule](#tarifs).

Lorsque Travel Rule est activé, cette requête est acheminée via le rail `EMAIL`, car elle indique l'e-mail de conformité de la contrepartie et aucun rail précédent ne peut atteindre le bénéficiaire :

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

La réponse, résumée aux champs importants ici :

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

* `originator_data` correspond à ce que vous avez envoyé, car le sujet de l'exemple n'a pas de vérification KYC approuvée ; pour un utilisateur vérifié, Didit le construit à partir des données vérifiées (voir [Données vérifiées et provenance](#données-vérifiées-et-provenance)).
  Le rail `EMAIL` n'affiche au destinataire que les champs du donneur d'ordre que vous partagez dans [`data_sharing_fields`](#partage-de-données).
* `beneficiary_data.chain` vaut `ETH` car la requête n'indiquait aucune chaîne ; Didit l'a déduite de la devise.
* `status` vaut `APPROVED` car aucune règle n'a agi sur la transaction ; vos propres règles peuvent lui attribuer un autre statut.
* Le contact de conformité reçoit le lien de récupération, et le transfert se termine, échoue au contrôle de correspondance du nom ou est refusé lorsque le contact répond, ou expire s'il ne répond pas (voir [Récupération par e-mail](#récupération-par-e-mail-pour-les-contreparties-injoignables)).

`protocol` reste `null` car le rail est choisi après la réponse (par exemple `EMAIL` lorsque vous avez fourni `counterparty_vasp_email`) : [Obtenir la transaction](https://docs.didit.me/fr/management-api/transactions/get.md) l'affiche une fois le rail choisi, et chaque webhook `travel_rule.status.updated` ultérieur le contient dans `rail`.
Si le portefeuille du bénéficiaire appartient à un autre client Didit qui en a prouvé le contrôle, le rail `INTERNAL` règle l'ensemble de l'échange juste après la réponse, et ce webhook indique déjà `COMPLETED` (ou `COUNTERPARTY_MISMATCHED_DATA`). Lorsque le nom ne correspond pas, votre transfert passe en `COUNTERPARTY_MISMATCHED_DATA` et l'autre client ne reçoit rien.
Sans `counterparty_vasp_email`, et lorsque ni les rails ni l'[attribution](#attribution-par-analyse-de-la-blockchain) ne peuvent atteindre le bénéficiaire, le transfert finit plus tard en `COUNTERPARTY_VASP_NOT_FOUND` avec une action `wallet_ownership` sur la transaction.

> [!NOTE]
>
> L'objet `travel_rule` intégré à la transaction contient les champs propres au transfert (`uuid`, `status`, `protocol`, `required`, `obligations_count`, `originator_data`, `beneficiary_data`, `metadata`), les éléments que Didit a déterminés et comparés (`chain_details`, `name_match`, `data_match`, `mismatched_fields`, ainsi que les champs de [masquage](#données-de-la-contrepartie-masquées) `data_masked` et `data_unmasked`), et les champs qui indiquent où en est le transfert : `direction`, `rail`, `counterparty_vasp`, `delivery`, `discovery`, `reason` et `next_actions` (voir [Pourquoi un transfert est bloqué](#pourquoi-un-transfert-est-bloqué)).
> Chaque ligne de [`GET /v3/transactions/`](https://docs.didit.me/fr/management-api/transactions/list) contient le même bloc, ou `null` pour une transaction sans transfert.
> Les endpoints de transfert dédiés ci-dessous renvoient aussi ces champs, ainsi que `ivms_version`, `ownership_confirmed`, `deadline_at`, `timeline` et `created_at`, mais ni `obligations_count` ni `metadata`.

Pour clôturer un transfert sortant `COMPLETED` une fois qu'il a été diffusé on-chain :

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

Cela exige que le transfert soit un transfert **sortant** au statut `COMPLETED`, sinon l'API renvoie `409`.
En cas de succès, le transfert sortant et le transfert correspondant sur le rail `INTERNAL` passent tous deux à `FINISHED`, et le `payment_txn_id` de la transaction est défini sur le hash que vous avez fourni.
Sur le rail `TRP`, Didit publie également le hash sur le callback de confirmation de la contrepartie.

Pour annuler un transfert qui n'a pas encore atteint un état final, envoyez plutôt `{"action": "cancel"}` au même endpoint.
Le transfert correspondant sur le rail `INTERNAL` est également annulé, et sur le rail `TRP`, une fois que la contrepartie a approuvé la demande, Didit publie l'annulation sur son callback de confirmation.
Un transfert déjà en `FINISHED`, `CANCELLED` ou `EXPIRED` renvoie `409`.
Une requête sans `payment_txn_id` ni `action` pris en charge renvoie `400`.

Sur un transfert passé par `GTR`, `VERIFYVASP` ou `SYGNA`, Didit signale le hash on-chain (ou l'annulation) au réseau après la réponse, avec les [mêmes mécanismes de nouvelle tentative](#livraison-et-nouvelles-tentatives) ; le champ `delivery` de la réponse indique ce message.

Pour relancer un transfert sortant qui n'a pas pu atteindre sa contrepartie (un transfert actuellement en `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE` ou `NOT_ENOUGH_COUNTERPARTY_DATA`), améliorez d'abord les données de la contrepartie, par exemple en ajoutant un `travel_address` à l'entrée du portefeuille de destination, un [contact](#contacts) pour l'entrée de la contrepartie dans l'annuaire, ou en ajoutant le VASP à l'[annuaire](#annuaire).
Envoyez ensuite `{"action": "resend"}` au même endpoint.
Didit relance le délai de confirmation et met à nouveau en file la séquence de routage depuis le début, en utilisant les données stockées sur le transfert : la réponse montre le transfert de nouveau en `AWAITING_COUNTERPARTY` (avec `resend queued` dans sa `timeline`), et le résultat arrive sous forme de webhook `travel_rule.status.updated`.
Une relance envoyée pendant qu'une tentative précédente est encore en cours est envoyée une fois cette tentative terminée.
Tout autre statut, ou un transfert entrant, renvoie `409`.

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

Pour désigner vous-même la contrepartie lorsque la [découverte](#découverte-de-la-contrepartie) n'a pas pu le faire, envoyez `{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"}` (l'`id` provenant de `GET /v3/travel-rule/vasps/`).
Le transfert est acheminé vers ce VASP, puis envoyé à nouveau.
Seul un transfert **sortant** en `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE` ou `NOT_ENOUGH_COUNTERPARTY_DATA` accepte cette action ; tout autre statut renvoie `409`, et un identifiant inconnu renvoie `404`.

### Pourquoi un transfert est bloqué

Un transfert en `COUNTERPARTY_VASP_NOT_FOUND` ou `COUNTERPARTY_VASP_NOT_REACHABLE` indique pourquoi il est bloqué et ce qui le fait avancer, à chaque lecture de sa transaction.
Le bloc `travel_rule` de [`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/fr/management-api/transactions/get), du `201` de [`POST /v3/transactions/`](https://docs.didit.me/fr/management-api/transactions/create) et de chaque ligne de [`GET /v3/transactions/`](https://docs.didit.me/fr/management-api/transactions/list) le contient, de même que l'objet de transfert renvoyé par le [endpoint de transfert](https://docs.didit.me/fr/management-api/travel-rule/update-transfer.md) :

* `reason` est la cause lisible par la machine, `""` pour tout autre statut.
* `next_actions` liste les actions qui font avancer le transfert, `[]` pour tout autre statut.
* `delivery` est le message le plus récent que le transfert doit envoyer à l'autre partie : `kind`, `status` (`QUEUED`, `SENDING`, `RETRYING`, `DELIVERED` ou `FAILED`), `rail`, `attempts`, `last_error`, `next_attempt_at` et `delivered_at`, ou `null` lorsqu'il n'en doit aucun.
* `discovery` indique où Didit a cherché la contrepartie, avec une entrée par étape exécutée : `step`, `outcome` (`match`, `miss` ou `skipped`), `reason`, `vasp`, `rail` et `at`.

`email_undeliverable` et `delivery_failed` proviennent d'une livraison échouée et n'apparaissent que sur `COUNTERPARTY_VASP_NOT_REACHABLE`.
Une livraison échouée détermine d'abord `reason`, de sorte qu'une contrepartie que Didit a trouvée n'est jamais présentée comme introuvable.
Les quatre autres proviennent de la trace de découverte, quel que soit le statut.

| `reason` | Ce qui s'est passé | `next_actions` |
| - | - | - |
| `email_undeliverable` | Le fournisseur de messagerie a refusé l'e-mail de conformité et il ne reste aucune adresse à essayer : au moment de l'envoi, lors d'une relance, ou sous la forme d'un rejet définitif signalé plus tard. | `update_compliance_email`, puis `resend` |
| `delivery_failed` | Le rail chargé de la livraison a échoué de façon définitive : un rail réseau a refusé le message ou épuisé ses tentatives, un hôte d'identité `TRP` n'a pas répondu, ou l'hôte `TRP` de la contrepartie n'a pas accepté la demande. | `resend` |
| `awaiting_end_user` | Aucune étape de découverte n'a désigné de contrepartie que Didit peut joindre : l'utilisateur final est invité à la nommer. | `select_counterparty_vasp`, `declare_self_hosted`, `add_vasp_hint`, `resend` |
| `directory_match_no_route` | Une étape a désigné une entrée de l'[annuaire](#annuaire), mais cette entrée n'a aucune route que Didit peut utiliser pour vous. | Identique à `awaiting_end_user` |
| `picked_member_without_proven_wallet` | L'utilisateur final a choisi un autre client de Didit comme contrepartie, mais ce client n'a pas prouvé qu'il contrôle le portefeuille. | Identique à `awaiting_end_user` |
| `no_directory_match` | La trace de découverte ne donne aucune autre raison. | Identique à `awaiting_end_user` |

| Action suivante | Que faire |
| - | - |
| `update_compliance_email` | Indiquez à nouveau l'e-mail de conformité de la contrepartie, avec `{"action": "update_compliance_email", "compliance_email": "..."}` sur l'endpoint du transfert. |
| `resend` | Relancez le routage avec `{"action": "resend"}` une fois que quelque chose a changé. |
| `select_counterparty_vasp` | L'utilisateur final choisit le VASP de destination dans le [widget de vérification de la propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md). `url` est le lien du widget tant que le transfert dispose d'une session de widget ouverte. |
| `declare_self_hosted` | L'utilisateur final déclare dans le widget que le portefeuille est auto-hébergé, à la même `url`. |
| `add_vasp_hint` | Indiquez à Didit qui est la contrepartie, avec un nom de VASP, un LEI ou une Travel Address (par exemple un `travel_address` sur l'[entrée du carnet d'adresses](#carnet-d’adresses-de-portefeuilles) du portefeuille de destination), puis relancez. |

Un renvoi fait avancer le transfert, donc son `reason` est effacé.
La ligne de la chronologie correspondant à une transition provoquée par une livraison échouée porte la même valeur sous `reason`.

Si le fournisseur de messagerie refuse l'e-mail de conformité du [transfert d'exemple ci-dessus](#envoyer-un-transfert), le bloc `travel_rule` de sa transaction se présente, en version abrégée :

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

Ici, `delivery` concorde avec `reason`, car l'envoi lui-même a été refusé.
Un refus lors d'une relance, ou un rejet signalé plus tard, laisse `delivery.status` à `DELIVERED` : le premier e-mail est bien parti.
Pour savoir pourquoi un transfert est bloqué, lisez `reason`, et non `delivery`.

Pour corriger, indiquez à nouveau l'e-mail de conformité de la contrepartie, puis relancez :

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

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

`update_compliance_email` conserve l'adresse comme `beneficiary_data.counterparty_vasp_email` de ce transfert : elle n'appartient qu'à ce transfert et n'est rattachée à aucune entrée d'annuaire ni à aucune liste de contacts.
Le statut ne change pas, la chronologie reçoit `compliance email updated`, et le transfert garde `reason: email_undeliverable` jusqu'à ce que le renvoi envoie la demande à la nouvelle adresse.
Seul un transfert sortant dans `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE` ou `NOT_ENOUGH_COUNTERPARTY_DATA` accepte cette action ; tout autre transfert reçoit une réponse `409`, et un `compliance_email` absent ou mal formé reçoit une réponse `400`.

### Portefeuilles auto-hébergés déclarés

Lorsque votre client vous indique que la destination est son propre portefeuille auto-hébergé (non hébergé), déclarez-le sur la transaction plutôt que de laisser le routage découvrir qu'aucun VASP ne se trouve derrière : définissez le type de moyen de paiement de la contrepartie sur `unhosted_wallet`.

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

La déclaration court-circuite entièrement le routage :

* Aucun rail n'est essayé, car il n'existe aucun VASP de contrepartie avec lequel échanger, et le transfert est créé directement dans `UNCONFIRMED_OWNERSHIP`.
* Lorsque `auto_wallet_verification` est activé (valeur par défaut), Didit génère automatiquement une session de [widget de vérification de la propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) liée à la transaction et la renvoie dans le bloc `action_required` de la transaction ; les SDK la lancent automatiquement lors de la [soumission côté client](https://docs.didit.me/fr/transaction-monitoring/sdk-transaction-submission.md).
* Dès que le client prouve le contrôle du portefeuille, le transfert effectue la vérification de correspondance du nom du bénéficiaire et passe à `COMPLETED` (ou à `COUNTERPARTY_MISMATCHED_DATA` si la correspondance échoue).

Cela correspond, sous forme d'API, à l'obligation du règlement TFR de l'UE relative aux portefeuilles auto-hébergés : vérifier la propriété du portefeuille non hébergé au lieu d'échanger des données avec un VASP de contrepartie.
La déclaration est lue dans le moyen de paiement côté **destination** d'un transfert sortant et côté **source** d'un transfert entrant : dans les deux cas, il s'agit du participant contrepartie.
Un transfert déclaré non hébergé suit déjà son chemin final sans rail, donc `UNCONFIRMED_OWNERSHIP` n'est pas un statut permettant un renvoi : terminez la preuve de propriété (ou annulez) au lieu d'appeler `action: "resend"`.

## Réception

### Confirmation de la propriété du portefeuille

Un transfert reste en `UNCONFIRMED_OWNERSHIP` dans deux cas :

* Un transfert entrant correspond à une entrée du [carnet d'adresses](#carnet-d’adresses-de-portefeuilles) dont la propriété n'est pas encore vérifiée.
* Le portefeuille de la contrepartie a été déclaré auto-hébergé (`counterparty.payment_method.method_type: "unhosted_wallet"`), pour un retrait sortant ou un dépôt entrant. Voir [Portefeuilles auto-hébergés déclarés](#portefeuilles-auto-hébergés-déclarés).

Votre application confirme ou refuse le transfert :

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

Le corps de la requête accepte `confirmed` (obligatoire, booléen) et un `wallet_address` facultatif.

L'endpoint accepte un transfert dans l'un de ces états et renvoie `409` avec `{"detail": "Transfer is not awaiting ownership confirmation."}` pour tout autre état :

* `UNCONFIRMED_OWNERSHIP`, dans les deux sens.
* `ON_HOLD` sur un transfert entrant.
* `UNCONFIRMED_OWNERSHIP`, `ON_HOLD` ou `COUNTERPARTY_MISMATCHED_DATA` sur un transfert entrant `TRP`, afin qu'un responsable de la conformité puisse traiter manuellement une demande de la contrepartie.

Sur un transfert créé par Didit lui-même (rail `INTERNAL`, enregistrement sunrise ou portefeuille auto-hébergé déclaré) :

* `confirmed: true` marque l'entrée correspondante du carnet d'adresses de portefeuilles comme propriété vérifiée, puis lance la correspondance de nom avec le `holder_name` de cette entrée : `COMPLETED` si elle réussit, `COUNTERPARTY_MISMATCHED_DATA` sinon.
  La contrepartie sur le rail `INTERNAL`, lorsqu'elle existe, reçoit le même résultat.
  Pour un portefeuille auto-hébergé déclaré, `confirmed: true` enregistre que votre client a vérifié qui contrôle le portefeuille de la contrepartie et termine le transfert (`COMPLETED`), en sortie comme en entrée.
  Un transfert entrant dont le dépôt a déjà été réglé passe directement à `FINISHED`.
* `confirmed: false` fait passer les deux côtés du transfert à `COUNTERPARTY_VASP_GENERAL_DECLINE`.
* L'entrée correspondante est recherchée dans votre propre carnet d'adresses uniquement par adresse de portefeuille, sans tenir compte de la chaîne.
  Résoudre un `ON_HOLD` entrant sans entrée correspondante crée l'entrée à partir de `beneficiary_data.wallet_address` du transfert et la marque comme vérifiée.
  Un portefeuille auto-hébergé déclaré fait exception : il est enregistré dans votre carnet d'adresses comme propriété vérifiée, comme décrit ci-dessous.
  Dans tous les autres cas, une entrée absente renvoie `409` avec `{"detail": "wallet address entry no longer available"}`, ce qui est aussi la réponse pour une entrée supprimée.

Sur un transfert entrant `TRP`, le même appel répond à la demande de la contrepartie :

* `confirmed: true` nécessite une adresse de paiement à renvoyer, prise dans le `wallet_address` de la requête, puis dans l'entrée correspondante, puis dans `beneficiary_data.wallet_address`.
  Si aucune des trois n'est disponible, la requête renvoie `400` avec `{"wallet_address": ["wallet_address is required to approve."]}`.
  Didit crée ou vérifie l'entrée, envoie à la contrepartie le rappel de résolution de la demande, définit `ownership_confirmed` et fait passer le transfert à `COMPLETED`.
* `confirmed: false` envoie une résolution de refus et fait passer le transfert à `COUNTERPARTY_VASP_GENERAL_DECLINE`.
* Si l'endpoint de rappel de la contrepartie est injoignable, la requête renvoie `502` et le transfert conserve son statut actuel.
  En cas d'approbation, l'entrée du carnet d'adresses du portefeuille est tout de même créée ou marquée comme vérifiée.

Pour un portefeuille auto-hébergé déclaré, le portefeuille que vous vérifiez est celui de la contrepartie : la destination d'un retrait sortant, ou la source (le portefeuille du donneur d'ordre) d'un dépôt entrant.
Didit l'ajoute au carnet d'adresses comme propriété vérifiée au nom de la contrepartie, de sorte que le portefeuille est comparé au nom de la contrepartie (le donneur d'ordre, pour un dépôt).
Si le carnet d'adresses contient déjà ce portefeuille sous un autre nom de titulaire, le transfert passe à `COUNTERPARTY_MISMATCHED_DATA` à la place.

Si le transfert ne désigne aucun portefeuille de contrepartie, transmettez `wallet_address` dans le corps de la requête.
Lorsque ni le transfert ni la requête ne fournissent d'adresse de portefeuille, la requête est rejetée avec `400`.

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

* Un transfert par réseau déjà traité sur son réseau (transfert VerifyVASP, ou transfert GTR ou Sygna Bridge déjà tranché) renvoie `409` (voir [Transferts depuis un réseau](#transferts-depuis-un-réseau)).

La réponse est l'objet de transfert plus complet décrit ci-dessus.
Un nouveau transfert `INTERNAL` n'attend jamais à cette étape : il n'atteint qu'un client ayant déjà prouvé le contrôle du portefeuille. Un ancien transfert `INTERNAL` encore en attente, antérieur à cette règle, masque les coordonnées de l'expéditeur tant que vous n'avez pas prouvé le contrôle du portefeuille (message signé ou dépôt test), et sa confirmation renvoie `409` jusque-là. Le refus reste possible.
Lorsque la contrepartie a indiqué que le portefeuille est auto-hébergé (aujourd'hui, le [Didit Test VASP](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) donne cette réponse en sandbox), la confirmation ajoute le portefeuille à votre carnet d'adresses au nom du bénéficiaire et lance la correspondance de nom : une correspondance termine le transfert et marque le portefeuille comme vérifié.

### Transferts depuis un réseau

Didit peut aussi recevoir des transferts Travel Rule qu'une contrepartie envoie via `GTR`, `VERIFYVASP` ou `SYGNA`.
La réception est activée par Didit pour chaque réseau séparément, et **aucun n'est activé aujourd'hui** : elle requiert aussi l'enregistrement des endpoints de Didit auprès de chaque réseau, et Didit ne dispose pas encore d'adhésion à ces réseaux.
Les transferts `TRUST` ne peuvent pas être reçus.

Une fois un réseau activé, un message qu'il délivre est traité ainsi :

* **Routage.** Le message vous parvient lorsqu'il désigne le code de participant de votre propre adhésion connectée sur ce réseau. Un message adressé à l'adhésion propre de Didit ne vous parvient que si vous êtes l'unique client Didit actif ayant prouvé le contrôle du portefeuille bénéficiaire. Tout autre message est refusé, afin qu'aucun client ne reçoive jamais le transfert d'un autre client.
* **Décision.** Didit crée le transfert entrant sur votre application et le décide comme une [demande TRP](https://docs.didit.me/fr/transaction-monitoring/travel-rule-interoperability.md#endpoints-trp) : correspondance du portefeuille, propriété, correspondance des noms et `inbound_auto_accept`.
* **Une seule fois.** Les réseaux renvoient un message tant qu'il n'a pas reçu de réponse. Une répétition reçoit la réponse de la première copie, et rien n'est créé ni décidé deux fois.
* **Votre examen, sur `GTR` et `SYGNA`.** Un transfert qui attend votre décision (`UNCONFIRMED_OWNERSHIP`, `ON_HOLD` ou `COUNTERPARTY_MISMATCHED_DATA`) se décide avec [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille) : `confirmed: true` l'approuve (transmettez `wallet_address` lorsque le transfert n'en désigne aucun) et `confirmed: false` le refuse. Didit répond au réseau après la réponse, avec les [mêmes nouvelles tentatives](#livraison-et-nouvelles-tentatives). Un transfert déjà traité renvoie `409`.
* **Pas de réponse ultérieure sur `VERIFYVASP`.** VerifyVASP obtient sa réponse au moment où il la demande : un transfert qui nécessiterait votre examen est refusé immédiatement et ne peut plus être décidé ensuite (`409`).

### Données de la contrepartie masquées

Tant que la propriété du portefeuille n'est pas confirmée, les données personnelles de la contrepartie d'un virement entrant sont renvoyées masquées plutôt qu'omises.
La première lettre de chaque mot est conservée, et les informations de portefeuille, de chaîne et d'acheminement VASP restent lisibles.
`data_masked` vaut `true`, et les données de la contrepartie dans le détail, la liste et l'export CSV de la transaction sont masquées de la même manière.

Une fois la propriété confirmée, les valeurs réelles apparaissent et `data_unmasked` enregistre l'entrée d'audit :

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

`reason` vaut `ownership_confirmed`, `wallet_proof`, `address_book_match`, `own_verified_wallet`, `inquiry_approved` (vous avez approuvé une demande `TRP` ou un message `GTR` ou `Sygna`), `known_payment_method` (le portefeuille de l'utilisateur final, présent dans vos registres, a répondu) ou `travel_address` (l'adresse Travel Address propre à chaque portefeuille a désigné ce portefeuille).
Un virement `INTERNAL` entrant porte `data_unmasked` avec `reason: wallet_proof` dès sa création, car seul un destinataire ayant prouvé la détention du portefeuille en reçoit un.
`actor` est l'e-mail de l'utilisateur de la console ou le libellé de la clé API, et reste vide pour une décision automatique.
Le rapport **Journal des virements Travel Rule** (CSV et PDF) masque de la même manière les noms d'un virement entrant tant que la propriété n'est pas confirmée. Les rapports que vous déposez auprès d'un régulateur, comme le XML goAML et le PDF du rapport réglementaire, contiennent les données réelles de la contrepartie.

### Retour sur les écarts

Un virement `COUNTERPARTY_MISMATCHED_DATA` indique désormais les champs qui diffèrent.
`data_match` contient la comparaison et `mismatched_fields` liste les champs.
Les deux ne contiennent que des noms de champs et des résultats, jamais des valeurs, afin de rester lisibles lorsque les données sont masquées :

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

La chronologie mentionne les champs, par exemple « la date de naissance du bénéficiaire ne correspond pas au titulaire du portefeuille ».
Un rejet `TRP` après un écart indique au VASP émetteur les champs qui diffèrent, par exemple « Les données du bénéficiaire ne correspondent pas au titulaire du portefeuille : nom ».
Ce que l'autre VASP apprend est volontairement limité : tant que le nom est incorrect, le nom est le seul champ signalé (`mismatched_fields: ["name"]`, tous les autres étant `not_compared`). Une fois le nom concordant, chaque champ qui diffère est nommé. Cela s'applique au côté émetteur d'un échange `INTERNAL` et à un rejet `TRP`.
Un nom absent des deux côtés est `not_compared` et ne constitue pas un écart, et un pays est comparé selon ce qu'il désigne, de sorte que `ES` et `ESP` concordent.

#### Score de correspondance du nom

Chaque virement dont le nom a été comparé porte `name_match`, et l'entrée de chronologie qui a tranché porte le même objet à côté de son `detail` :

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

* `score` est la similarité des deux noms complets, de 0 à 100, et vaut `0` lorsque l'un des deux est vide. `matched` est le résultat au niveau `strictness`, de sorte que les deux peuvent diverger : `FUZZY` tranche sur son propre ratio de similarité de `0.85`.
* `matched_names` sont les mots du nom du virement retrouvés dans l'autre nom ; `mismatched_names` sont les mots que l'un des deux côtés possède et que l'autre n'a pas.
* `method` vaut `none`, `exact_token`, `token_subset`, `token_containment` ou `sequence_ratio`, pour `NONE`, `STRICT`, `DEFAULT`, `CONTAINS` et `FUZZY`.
* `compared_with` vaut `wallet_holder` (le titulaire enregistré pour le portefeuille) ou `counterparty_vasp` (le nom donné par la contrepartie sur la page de réponse par e-mail, ou la réponse du Didit Test VASP).
* L'issue du virement suit `data_match`, qui compare chaque champ. Un virement peut donc être `COUNTERPARTY_MISMATCHED_DATA` alors que `name_match.matched` vaut `true`.
* `name_match` figure dans l'objet virement renvoyé par les endpoints Travel Rule et dans `travel_rule` intégré à la transaction, et vaut `null` tant qu'aucun nom n'a été comparé. La liste des virements et les webhooks ne le contiennent pas.
* Les noms sont masqués tant que les parties sont [masquées](#données-de-la-contrepartie-masquées), et sur la copie de l'autre client d'un échange `INTERNAL`.

### Messages et demandes de documents

Deux VASP peuvent résoudre un virement contesté dans Didit plutôt que par e-mail.
`GET` et `POST /v3/transactions/{transaction_id}/travel-rule/messages/` listent et envoient des messages sur un virement :

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

* `kind` vaut `MESSAGE` (par défaut) ou `DOCUMENT_REQUEST`. Une demande de document exige au moins une entrée dans `requested_documents` (jusqu'à 10) ; un message exige un `body` (jusqu'à 5000 caractères) ou un fichier.
* `reply_to` désigne un message antérieur du même virement. Une réponse de l'autre côté qui contient un document satisfait la demande de document à laquelle elle répond, et `fulfilled_at` y est renseigné. Votre propre réponse ne satisfait jamais votre propre demande.
* Envoyez en multipart avec `files` pour joindre jusqu'à 5 fichiers de 10 Mo chacun, au format PDF, PNG, JPEG ou WebP. Didit vérifie le contenu de chaque fichier, et pas seulement son type déclaré. Tout autre fichier renvoie `400` sur `files` (« Joignez uniquement des fichiers PDF, PNG, JPEG ou WebP. »).
* Un virement peut contenir jusqu'à 100 messages, dont jusqu'à 20 peuvent porter des pièces jointes (chacune avec jusqu'à 5 fichiers), en comptant les deux côtés. Au-delà de l'une ou l'autre limite, la requête renvoie `400`. Les pièces jointes des messages ne comptent pas dans les documents propres à la transaction.
* Les messages fonctionnent sur les canaux `INTERNAL` et `EMAIL`. Sur `INTERNAL`, l'autre client Didit voit le message de son côté de la paire. Sur `EMAIL`, la contrepartie reçoit un e-mail et répond dans le portail de retrait. Si cet e-mail de notification ne peut pas être envoyé, le message est tout de même enregistré et la requête renvoie `201`.
* La requête renvoie `409`, avec une raison dans `detail`, pour un virement sur `TRP`, `GTR`, `TRUST`, `VerifyVASP` ou `Sygna` (ces protocoles ne transportent pas de messages), un virement sans contrepartie pour l'instant, un virement `INTERNAL` dont les données ne correspondaient pas au titulaire du portefeuille (rien n'a atteint le destinataire), un virement `INTERNAL` dont le destinataire n'a pas encore prouvé le contrôle du portefeuille (des deux côtés, tant qu'il ne l'a pas fait), un virement `EMAIL` dont la contrepartie n'a plus d'adresse e-mail de conformité, et un virement `EMAIL` dont le lien de retrait a expiré ou dont le virement est clos.
* Aucun webhook n'est déclenché lorsque la contrepartie répond. Consultez l'endpoint des messages.
* L'e-mail qui informe la contrepartie d'un nouveau message est envoyé en anglais.

### Carnet d'adresses de portefeuilles

Enregistrez les portefeuilles que vous contrôlez afin que les virements entrants (canal `INTERNAL`, demandes `TRP` entrantes et [enregistrements après dépôt](#enregistrement-a-posteriori-sunrise)) puissent être rapprochés de ces portefeuilles, avec `POST`/`GET /v3/travel-rule/wallet-addresses/` et `PATCH`/`DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/`.

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

| Champ | Type | Description |
| - | - | - |
| `address` | string | L'adresse du portefeuille. Unique par application, `chain` et `memo` (l'adresse est comparée sans tenir compte de la casse) : un doublon renvoie `400`. Une adresse X d'XRP est stockée sous sa forme classique et son tag. Vide pour une entrée [importée par son hachage](#import-en-masse-de-portefeuilles). |
| `address_hash` | string | Lecture seule. SHA-256 de l'adresse en minuscules, au format hexadécimal minuscule. |
| `memo` | string | Facultatif. Le tag de destination ou le mémo qui fait partie du portefeuille sur un [réseau à mémo](#réseaux-à-mémo-et-à-tag) ; vide pour l'ensemble de l'adresse. Alias : `destinationTag`. |
| `chain` | string | Identifiant de chaîne facultatif, jusqu'à 32 caractères. Utilisez le même token que vos transferts (par exemple `ETH` ou `BTC`), ou laissez-le vide pour faire correspondre les transferts sur n'importe quelle chaîne, voir la note ci-dessous. Un bénéficiaire Lightning (identifiant de nœud ou Lightning Address) enregistré sur bitcoin est stocké en minuscules sur `LIGHTNING`, où les transferts [Lightning](#lightning) sont appariés. |
| `holder_name` | string | Nom du titulaire du portefeuille, utilisé pour la correspondance du nom du bénéficiaire. |
| `holder_vendor_data` | string | Votre identifiant interne pour le titulaire. |
| `entity_type` | string | Texte libre jusqu'à 16 caractères, valeur par défaut `"individual"`. |
| `travel_address` | string | Facultatif. La Travel Address du VASP de la contrepartie pour ce portefeuille, si vous la connaissez. Permet à Didit d'acheminer les transferts vers ce bénéficiaire via le rail `TRP` plutôt que par e-mail. |
| `receiving_travel_address` | string | Lecture seule. Votre propre Travel Address propre à ce portefeuille. Communiquez-la à un VASP qui envoie vers ce portefeuille : sa demande TRP désigne alors directement le portefeuille. Vide pour un portefeuille enregistré comme celui d'un autre VASP, tant que la Travel Rule est désactivée, et avant l'enregistrement de votre profil VASP. Elle cesse de répondre une fois le portefeuille supprimé. |
| `is_ownership_verified` | boolean | Lecture seule. Indique si l'actionnariat de cette adresse a été prouvé (via le [widget de vérification de l'actionnariat du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) ou par auto-déclaration). |
| `self_declared` | boolean | Écriture seule, `POST` uniquement. Lorsque `true`, l'entrée est créée déjà vérifiée via une preuve `SELF_DECLARATION` : les transferts entrants vers cette adresse ignorent entièrement `UNCONFIRMED_OWNERSHIP` et passent directement à la correspondance du nom. Renvoie `400` lorsque `allow_self_declaration` est désactivé dans vos paramètres. |

`PATCH` accepte `holder_name`, `holder_vendor_data`, `entity_type` et `travel_address`, et ne renvoie que ces quatre champs.
`DELETE` supprime l'entrée (`204`) : son adresse est libérée pour un nouvel enregistrement et elle cesse immédiatement de correspondre aux nouveaux transferts.

> [!NOTE]
>
> La correspondance ne tient pas compte de la casse pour l'adresse, inclut son `memo` sur un réseau à mémo, et est limitée par `chain`.
> Lorsqu'un transfert porte une chaîne, une entrée ne correspond que si son `chain` a la même valeur (sans tenir compte de la casse) ou si cette valeur est vide.
> Les noms de chaînes sont comparés comme du texte, sans traduction : une entrée enregistrée sous `ethereum` ne correspond pas à un transfert dont la chaîne est `ETH`.
> Les transferts sortants tirent leur chaîne de `beneficiary_data.chain`, puis de `transaction_details.crypto_params.crypto_chain`, puis de `custom_properties.crypto_chain`, puis de la devise (par exemple `ETH`, ou pour `USDT` et `USDC` la chaîne déduite de `payment_reference_id`), uniquement lorsqu'il s'agit d'une chaîne reconnue par Didit ; les enregistrements faits après le dépôt prennent leur chaîne dans leur champ `chain`, en minuscules.
> Les demandes `TRP` entrantes ignorent la chaîne : elles correspondent par adresse ou, lorsque la demande ne nomme aucun portefeuille, par nom du bénéficiaire parmi vos entrées vérifiées.
> [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille) ne correspond aussi que par adresse.
> Lorsque plusieurs entrées correspondent, le rail `INTERNAL` et les enregistrements faits après le dépôt privilégient une entrée dont l'actionnariat est vérifié, puis la plus ancienne ; la recherche `TRP` sortante prend l'entrée la plus ancienne qui stocke une Travel Address.
> Les demandes `TRP` entrantes et l'endpoint d'actionnariat prennent l'une quelconque des entrées correspondantes ; gardez donc une seule entrée par adresse lorsque c'est possible.
> Le rail `INTERNAL` ne correspond à aucune entrée lorsque des entrées vérifiées existent pour l'adresse dans plus d'une autre application.

#### Import en masse de portefeuilles

Importez jusqu'à 10 000 portefeuilles en un seul appel avec [`POST /v3/travel-rule/wallet-addresses/import/`](https://docs.didit.me/fr/management-api/travel-rule/import-wallet-addresses), au format JSON (`{"entries": [...]}` ou un tableau nu) ou sous forme de fichier CSV UTF-8 d'au plus 5 Mo avec une ligne d'en-tête.
Une ligne désigne un portefeuille par son `address`, par `address_hash` (le SHA-256 de l'adresse en minuscules, soit 64 caractères hexadécimaux ou 44 caractères base64), ou par les deux, ainsi que n'importe lequel des champs d'entrée ci-dessus. Envoyez uniquement le hachage pour enregistrer un portefeuille sans divulguer son adresse : il correspond aux transferts vers cette adresse comme n'importe quelle autre entrée, et peut être prouvé avec le [widget de vérification de l'actionnariat du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md).

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

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

L'import est idempotent et renvoie un statut pour chaque ligne, dans l'ordre d'envoi : `created`, `existing` (le même portefeuille, la même chaîne et le même memo figurent déjà dans le carnet et n'ont pas été modifiés), `duplicate` (répète une ligne précédente, désignée dans `duplicate_of_row`) ou `failed` avec ses `errors`.
Une ligne en erreur ne fait jamais échouer les autres : la réponse est `200` lorsqu'aucune ligne n'a échoué et `207` lorsqu'une ligne a échoué, avec les compteurs dans `total`, `created`, `existing`, `duplicate` et `failed`.
Une cellule CSV vide est considérée comme non envoyée, et les colonnes que l'import ne lit pas sont ignorées ; un export provenant d'un autre outil s'importe donc tel quel.

Sur `GET /v3/travel-rule/transfers/`, chaque ligne contient `originator_memo` et `beneficiary_memo` à côté des deux portefeuilles.

### Récupération par e-mail pour les contreparties injoignables

Lorsqu'aucun rail de protocole ne peut atteindre la contrepartie, Didit se rabat sur `EMAIL` : il envoie un lien de récupération à usage unique à `beneficiary_data.counterparty_vasp_email` (ou à une adresse déterminée à partir de `beneficiary_data.counterparty_vasp_name` dans le registre de l'annuaire VASP).
Le destinataire n'a besoin ni d'un compte Didit ni d'une clé API : le token du lien sert d'authentification.
Déroulement de l'envoi :

* **Aux bonnes adresses.** Une `counterparty_vasp_email` que vous transmettez est le seul destinataire. Sinon, Didit écrit à trois adresses au maximum : les [contacts](#contacts) de la contrepartie qui n'ont pas rebondi (les vôtres d'abord, puis ceux de la plateforme, en donnant la priorité à ceux qui ont déjà répondu), puis l'e-mail de conformité de l'entrée de l'annuaire lorsqu'un emplacement reste libre. Chaque adresse reçoit son propre e-mail.
* **En votre nom.** L'e-mail provient de l'adresse de Didit sous le nom « *votre nom légal* via Didit », dans la langue de la juridiction de la contrepartie (l'anglais lorsqu'elle n'est pas connue), et le lien ouvre la page de réponse dans cette langue. Vous ne recevez aucune copie : suivez la chronologie du transfert.
* **Avec des relances.** Tant que la contrepartie n'a pas répondu, Didit la relance 72, 24 et 6 heures avant l'échéance, au moins 12 heures après la demande ou la dernière relance, et jamais après l'échéance.
* **Avec la gestion des rebonds.** Un rebond ou une plainte pour spam marque le contact atteint comme invalide, avec sa raison, et la chronologie du transfert le consigne. Un contact de la plateforme est alors invalide pour tous les clients de Didit ; le contact propre à votre application ne l'est que pour votre application. Lorsque l'envoi d'e-mails de Didit cesse totalement pour une adresse (après un rebond permanent, une plainte pour spam ou un désabonnement), chaque contact à cette adresse est marqué invalide (`suppressed`), et ces contacts redeviennent valides dès que l'envoi vers l'adresse reprend. Lorsqu'il ne reste aucune adresse à laquelle écrire, le transfert passe à `COUNTERPARTY_VASP_NOT_REACHABLE`, avec la raison du serveur de messagerie destinataire dans sa chronologie. Un rebond permanent ou une plainte pour spam sur la `counterparty_vasp_email` que vous avez transmise produit cet effet immédiatement, car c'est le seul destinataire ; un rebond temporaire (`soft_bounce`) sur cette adresse, ou sur l'e-mail de conformité de l'entrée de l'annuaire, laisse la demande ouverte pour les relances.

Le lien ouvre une page hébergée qui affiche l'échange en attente et appelle, pour le destinataire, ces deux endpoints publics.
Pour le [transfert d'exemple ci-dessus](#envoyer-un-transfert), les détails de récupération sont :

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

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

`reference` correspond à l'`uuid` du transfert. `originator_data` et le `ivms_request` IVMS 101 qui en est construit ne contiennent que les champs du donneur d'ordre que vous partagez dans [`data_sharing_fields`](#partage-de-données) ; le nom et le portefeuille sont toujours inclus. `fiat_amount` et `fiat_currency` sont vides lorsque vous n'avez envoyé aucune valeur convertie, `contact` reprend la valeur de `?contact=` du lien, et `decline_reasons` liste les valeurs acceptées par `decline_reason`. `join_invitation.eligible` vaut `true` tant que la contrepartie dispose d'une invitation active à rejoindre Didit ; le lien d'invitation lui-même n'est transmis que par e-mail.

Cet endpoint renvoie le montant enregistré tel quel, avec les 18 décimales complètes, et non la forme tronquée utilisée par l'objet de détail de la transaction.

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

`decision` vaut `accept` ou `decline`.
En cas d'`accept`, le `beneficiary_name` fourni est comparé au `beneficiary_data.name` du transfert selon votre règle de nom : `COMPLETED` en cas de correspondance, `COUNTERPARTY_MISMATCHED_DATA` sinon. La contrepartie peut aussi envoyer une réponse IVMS 101 complète sous la forme de `ivms_response` ; son nom de bénéficiaire prévaut alors sur `beneficiary_name`.
En cas de `decline`, le transfert passe à `COUNTERPARTY_VASP_GENERAL_DECLINE`. La contrepartie peut indiquer un `decline_reason` parmi `decline_reasons`, accompagné d'un `decline_detail` (512 caractères maximum, obligatoire pour `other`) ; la chronologie de votre transfert l'enregistre, par exemple `counterparty declined: The beneficiary is not our customer`.
La réponse contient le `{"status": "..."}` résultant, plus `decline_reason` après un refus. Une fois que la contrepartie a répondu, les deux endpoints renvoient `410`.
Les deux endpoints renvoient `404` pour un token inconnu et `410` une fois la fenêtre close : soit le transfert a quitté `AWAITING_COUNTERPARTY`, soit son échéance de confirmation (`confirmation_timeout_hours` après la création ou le dernier renvoi) est passée.

La contrepartie lit et répond via le lien de récupération avec `GET` et `POST /v3/travel-rule/pickup/{token}/messages/`.
Cet endpoint ne nécessite aucune clé API, n'accepte que les pièces jointes PDF, PNG, JPEG ou WebP, et renvoie `410` une fois le lien expiré ou le transfert clos.
Un message que vous avez envoyé affiche le nom de votre VASP comme `author` et comme `uploaded_by` de la pièce jointe, jamais la personne ou la clé API à l'origine de l'envoi.

### Dépôts non rapprochés

Un dépôt entrant peut arriver avant les données Travel Rule du VASP d'origine.
Un tel dépôt reste en `AWAITING_COUNTERPARTY` ou `NOT_ENOUGH_COUNTERPARTY_DATA` tant que les données n'ont pas été reçues.
Listez les dépôts encore en attente avec une clé API disposant de l'autorisation transactions :

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

La réponse utilise les mêmes lignes que `GET /v3/travel-rule/transfers/`, limitées aux dépôts entrants en attente des données du VASP d'origine.

Lorsque les données sont arrivées sous la forme d'un message Travel Rule reçu distinct, rapprochez-les vous-même :

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

`{transaction_uuid}` désigne le dépôt.
`transaction_id` désigne la transaction du message Travel Rule reçu.
L'appel renvoie `{ "deposit": ..., "message": ... }`.
Il renvoie `404` lorsque l'une des deux transactions n'existe plus, et `409` avec `travel_rule_match_conflict` (« This deposit can no longer be matched. ») lorsque l'une ou l'autre des transactions a déjà changé d'état.

### Enregistrement a posteriori (sunrise)

Lorsqu'un dépôt crypto est réglé on-chain **avant** tout échange Travel Rule, parce que le VASP émetteur était injoignable ou que vous reprenez des dépôts historiques, enregistrez-le a posteriori avec `POST /v3/travel-rule/inbound/`.

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

`originating_vasp.travel_address` doit être une [Travel Address](#travel-addresses) réelle et valide au regard de sa somme de contrôle : une valeur factice renvoie `400` avec `"Invalid travel address."`.
`originating_vasp.lei` doit être vide ou comporter 20 caractères au format LEI (18 lettres ou chiffres, suivis de 2 chiffres) ; toute autre valeur renvoie `400` avec `"Invalid LEI: expected 20 alphanumeric characters (ISO 17442)."`.
Didit vérifie uniquement ce format, pas les chiffres de contrôle ISO 17442.

| Champ | Type | Description |
| - | - | - |
| `chain` | string | Chaîne sur laquelle le dépôt a été réglé, jusqu'à 32 caractères. Didit la met en minuscules et s'en sert pour faire correspondre les entrées de votre [carnet d'adresses](#carnet-d’adresses-de-portefeuilles) : utilisez donc le même token que vos entrées (`eth` correspond à une entrée enregistrée sous `ETH`). Elle détermine aussi si le dépôt peut être filtré : `bitcoin`/`btc`, `ethereum`/`eth`, `solana`/`sol`, `tron`/`trx`, `polygon`/`matic`, `bsc`/`bnb`, `litecoin`/`ltc`, `ripple`/`xrp`, `bitcoin-cash`/`bch` et `dogecoin`/`doge`. |
| `txid` | string | Hash de la transaction on-chain. Sert à dédupliquer : le même `txid` + `wallet_address` renvoie le transfert existant au lieu d'en créer un doublon. |
| `wallet_address` | string | Le portefeuille de destination sur lequel vous avez reçu le dépôt. Une adresse X de XRP est séparée en son adresse classique et son tag. |
| `memo` | string | Facultatif. Le tag de destination ou le memo du dépôt sur un [réseau à memo](#réseaux-à-mémo-et-à-tag) : sur une adresse de dépôt partagée, il désigne le client auquel appartient le dépôt. |
| `amount` | string (decimal) | Montant du dépôt. |
| `currency` | string | Actif du dépôt. |
| `originator_data` | object | Informations sur le donneur d'ordre transmises par le VASP émetteur, si elles sont connues, sous forme d'objet plat : Didit lit `name` (ou `full_name`), `first_name`, `last_name` et `vendor_data`. |
| `beneficiary_data` | object | Informations sur le bénéficiaire (votre client), sous forme d'objet plat : Didit compare son `name` au `holder_name` de l'entrée correspondante du carnet d'adresses, et y ajoute `wallet_address` et `chain`. |
| `utxo` | object | Facultatif, uniquement pour les [chaînes UTXO](#transferts-utxo). Les `inputs` et `outputs` de la transaction de dépôt : le portefeuille de l'expéditeur est `originator_data.wallet_address` lorsque vous le déclarez, sinon la plus grande entrée. |
| `originating_vasp` | object | Facultatif, `{ name, lei, travel_address }` décrivant l'expéditeur. `lei` doit respecter le format LEI ci-dessus et `travel_address` doit être une Travel Address valide, faute de quoi la requête renvoie `400`. Conservé sur ce transfert comme `metadata.originating_vasp` ; il ne crée ni ne modifie jamais une entrée de [répertoire](#comment-les-entrées-sont-ajoutées-à-l’annuaire) partagé. |

Didit crée une transaction `travelRule` entrante (ou réutilise un dépôt entrant déjà soumis avec le même hash et le même portefeuille, s'il n'a pas encore de transfert Travel Rule) et rapproche le transfert de votre [carnet d'adresses de portefeuilles](#carnet-d’adresses-de-portefeuilles), à partir de l'adresse et de son memo :

* Aucune entrée correspondante -> `ON_HOLD`.
  Résolvez-la avec [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille) : `confirmed: true` ajoute le portefeuille à votre carnet d'adresses comme vérifié et lance la correspondance du nom ; en cas de correspondance, le transfert passe à `COMPLETED`, puis à `FINISHED`.
* L'entrée existe mais la propriété n'est pas prouvée -> `UNCONFIRMED_OWNERSHIP`.
* Le nom du bénéficiaire ne correspond pas au `holder_name` de l'entrée -> `COUNTERPARTY_MISMATCHED_DATA`.
* Entrée vérifiée et nom correspondant -> `COMPLETED`, puis immédiatement `FINISHED` (le dépôt a déjà été réglé).

Avec l'entrée vérifiée du carnet d'adresses enregistrée dans [l'exemple ci-dessus](#carnet-d’adresses-de-portefeuilles), la réponse est :

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

Le `txn_id` de la nouvelle transaction est `tr-in-`, suivi des 80 premiers caractères du `txid` et d'un hash de 12 caractères calculé à partir du `txid` et du portefeuille.
`created` vaut `true` (HTTP `201`) lorsque cet appel a enregistré le dépôt, et `false` (HTTP `200`) lorsque le même `txid` et le même `wallet_address` étaient déjà enregistrés ; dans ce cas, le transfert existant est renvoyé sans modification.
Sur le chemin avec entrée vérifiée, les deux transitions ont lieu avant que l'endpoint ne réponde : la réponse synchrone affiche donc déjà le statut final `FINISHED` - l'étape intermédiaire `COMPLETED` reste néanmoins enregistrée dans le `timeline` du transfert.
Chaque transition déclenche un webhook `travel_rule.status.updated` (`COMPLETED` et `FINISHED` en déclenchent chacun un).
Lorsque Didit a créé une nouvelle transaction, elle envoie d'abord [`transaction.created`](https://docs.didit.me/fr/integration/webhooks#types-d’événements), puis ces événements.
L'enregistrement n'entraîne ni frais Travel Rule ni frais de surveillance.
Lorsque la surveillance crypto est activée et que le `chain` est un réseau que Didit peut filtrer, un dépôt nouvellement créé est également filtré et facturé une seule fois au tarif Wallet Verification & Screening.

### Widget de propriété du portefeuille

Pour faire passer un transfert hors de `UNCONFIRMED_OWNERSHIP`, ou pour vérifier un portefeuille à l'avance avant l'arrivée de tout transfert, générez un widget hébergé de propriété de portefeuille avec `POST /v3/travel-rule/widget-session/` et envoyez votre client à l'`url` renvoyée. Le widget prouve le contrôle du portefeuille par signature de message (y compris pour les comptes de contrat intelligent EVM, via ERC-1271 et ERC-6492), par un test de Satoshi, une capture d'écran ou une auto-déclaration, puis marque l'entrée correspondante du carnet d'adresses comme vérifiée afin que l'échange puisse se poursuivre.
Une capture d'écran fait exception : elle ne vérifie jamais le portefeuille à elle seule. Votre équipe de conformité l'examine donc et confirme un transfert en `UNCONFIRMED_OWNERSHIP` avec [`POST .../travel-rule/ownership/`](#confirmation-de-la-propriété-du-portefeuille).

Consultez [Widget de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md) pour le parcours complet, les méthodes de preuve et la remarque importante : le widget doit être ouvert dans un navigateur réel, jamais dans une webview d'un SDK mobile.

## Annuaire

`GET /v3/travel-rule/vasps/` interroge l'annuaire des VASP : les entrées de l'annuaire partagé ainsi que les membres du [réseau Didit](#le-réseau-didit). Chaque ligne indique comment votre application peut joindre ce VASP aujourd'hui.

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

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

`search` correspond à un fragment de nom ou d'alias, à un LEI exact, ou à un domaine (`counterparty.example` ou une URL de ce domaine). Affinez les résultats avec :

| Paramètre | Effet |
| - | - |
| `network` | `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` ou `TRISA` : uniquement les entrées que l'annuaire de ce réseau répertorie actuellement. |
| `jurisdiction` | Code pays ISO 3166 à deux ou trois lettres (alpha-2 ou alpha-3). |
| `reachable` | `true` : uniquement les VASP auxquels votre application dispose d'une route disponible. `false` : uniquement ceux qu'elle ne peut pas joindre aujourd'hui. |
| `kind` | `directory` pour les entrées partagées, `didit` pour les membres du réseau Didit. |
| `risk` | `LOW`, `MEDIUM` ou `HIGH` : uniquement les entrées de l'annuaire dont `risk_level` correspond à ce niveau. Voir [Score et niveau de risque](#score-et-niveau-de-risque). |
| `responsiveness` | `RESPONSIVE`, `UNRESPONSIVE` ou `UNKNOWN` : uniquement les entrées de l'annuaire avec ce `responsiveness.status`. Voir [Réactivité](#réactivité). |

Les membres du réseau Didit ne sont pas notés : `risk` et `responsiveness` ne renvoient que des entrées de l'annuaire. Les résultats sont triés par nom et paginés avec `limit` (par défaut `50`) et `offset`. Un réseau, un code pays, un niveau de risque ou un statut de réactivité inconnu renvoie `400` en nommant le champ. `GET /v3/travel-rule/vasps/{vasp_id}/` renvoie une seule ligne à partir de son `id`. Votre propre profil de VASP n'apparaît jamais dans vos résultats.

`dd_status`, `dd_score`, `dd_score_breakdown`, `dd_assessed_at`, `dd_score_semantic`, `risk_level`, `responsiveness` et `essential_checks` contiennent l'évaluation de vigilance partagée d'une entrée de l'annuaire. `decision`, `entity_screening` et `questionnaires` sont les dossiers propres à votre application concernant le VASP, et aucun autre client ne les voit. Voir [Vigilance sur les contreparties](#diligence-raisonnable-sur-la-contrepartie) pour la signification de chacun, et utilisez ces champs comme conditions avec les [champs de règle de contrepartie](#champs-de-règle-et-préréglages) plutôt que de les tester directement dans le code. Les membres du réseau Didit ne sont pas notés : `dd_status` vaut `COMPLETED`, `dd_score` vaut `null`, `risk_level` est vide et le détail du score est vide.

### Routes

`routes` liste les moyens par lesquels votre application peut livrer au VASP, dans l'ordre dans lequel l'échelle de routage les essaie. `reachable` vaut `true` lorsqu'au moins une route est `available` :

* `TRP` lorsque l'entrée comporte un point de terminaison TRP vérifié par l'annuaire.
* `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` lorsqu'un réseau répertorie le VASP sous un code. La route n'est `available` que si votre application peut envoyer via ce réseau aujourd'hui (`via: own_membership` pour votre propre adhésion connectée, `via: didit_membership` pour celle de Didit). Sinon, elle est `unavailable` avec `reason: no_membership` ou `reason: network_switched_off`, et n'est jamais présentée comme un moyen d'atteindre le VASP. Les adhésions de Didit aux réseaux ne sont pas encore opérationnelles : aujourd'hui, une route réseau n'est donc disponible que via une adhésion que vous avez connectée dans la Marketplace.
* `EMAIL` lorsqu'il existe un contact de conformité pour l'entrée.
* `INTERNAL` pour un membre du réseau Didit.

Une inscription `TRISA` apparaît sous `networks` et vous indique qui est le VASP sur TRISA, mais Didit ne dispose pas de canal TRISA : elle n'est donc jamais une route.

Une application sandbox n'envoie qu'au [VASP de test Didit](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md). Aucune route de l'annuaire ne lui est donc disponible : chaque route est `unavailable` avec `reason: sandbox_application`, chaque ligne a `reachable: false`, et `reachable=true` ne renvoie aucune ligne.

### Marques et leurs entités

Une marque qui opère via plusieurs entités réglementées est une seule ligne dont `entities` désigne les entités juridiques qui reçoivent ses transferts ; une ligne d'entité renvoie à sa marque dans `umbrella`. Un transfert qui désigne la marque est acheminé vers l'une de ses entités, choisie parmi toutes : celle de la juridiction indiquée dans `beneficiary_data.counterparty_vasp_jurisdiction`, sinon l'entité liée au portefeuille de destination par l'[attribution d'analyse blockchain](#attribution-par-analyse-de-la-blockchain), sinon l'unique entité de la marque lorsqu'aucune juridiction n'est indiquée. Ce n'est qu'ensuite que cette entité doit être joignable ; si elle ne l'est pas, le transfert ne part jamais vers une autre entité ni vers la marque à la place. Lorsqu'aucune entité ne convient (deux conviennent à égalité, ou la juridiction indiquée ne correspond à aucune d'elles), la route propre à la marque est utilisée ; sans route, le transfert est `COUNTERPARTY_VASP_NOT_FOUND`. Le choix de l'entité juridique qui reçoit les données du donneur d'ordre n'est jamais une supposition, et n'est jamais déterminé par l'entité qui dispose par hasard d'un contact.

Plusieurs entrées portant le même nom, comme des sociétés d'un groupe partageant un nom commercial, sont départagées par la même règle : celle de la juridiction indiquée, sinon celle à laquelle l'attribution d'analyse blockchain renvoie. Lorsque ni l'une ni l'autre n'en désigne une, aucune n'est retenue, et les détails sont considérés comme ne désignant aucune entrée de l'annuaire.

### Contacts

`contacts` sont les adresses auxquelles la route `EMAIL` envoie les e-mails. Les contacts `scope: platform` servent tous les clients Didit : Didit les sélectionne (`source: staff`), ou une adresse sur l'un des domaines propres au VASP a répondu à une demande via son lien de réponse (`source: learned`). Les contacts `scope: application` sont privés à votre application : ceux que vous ajoutez (`source: application`), et une adresse hors des domaines propres au VASP qui a répondu à l'une de vos demandes (`source: learned`). Un transfert envoie d'abord un e-mail au contact propre à votre application, puis à un contact de plateforme ayant déjà répondu (`verified_at` renseigné), puis aux autres.

Gérez les contacts propres à votre application avec l'API des contacts de contrepartie :

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

* `GET /v3/travel-rule/counterparty-contacts/` liste chaque contact auquel votre application peut écrire, y compris les contacts de plateforme et ceux qui ont rebondi, du plus récent au plus ancien, paginés avec `limit` et `offset`. Filtrez avec `?counterparty=` et l'`id` d'une entrée de l'annuaire ; une valeur qui n'est pas un UUID renvoie `400`.
* `POST` ajoute un contact privé (`counterparty`, `email`, `name` facultatif) et renvoie `201`. Ajouter une adresse que l'entrée possède déjà parmi vos propres contacts renvoie ce contact. Une entrée inconnue renvoie `400` (`"No directory entry with this id."`).
* `DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/` supprime l'un de vos propres contacts (`204`). Les contacts de plateforme ne peuvent pas être supprimés et renvoient `404`.

Un contact dont l'e-mail a rebondi ou fait l'objet d'une plainte porte `is_invalid: true`, avec `invalid_reason` (`hard_bounce`, `soft_bounce`, `spam_complaint` ou `suppressed`), `invalid_detail` et `invalid_at`, et le rail `EMAIL` l'ignore. `created_by` est vide sur les contacts de la plateforme et sur les contacts ajoutés par une clé API.

### Comment les entrées sont ajoutées à l'annuaire

* Le registre intérimaire MiCA de l'ESMA des prestataires de services sur crypto-actifs, consulté chaque semaine : le nom de chaque prestataire, son LEI et son État membre d'origine.
* L'annuaire public TRISA, consulté chaque semaine : qui est chaque membre, son pays et son inscription TRISA. Toute inscription que l'annuaire n'affiche plus pendant trois semaines est supprimée.
* Le registre mondial LEI complète le LEI, les autres raisons sociales et le pays d'une entrée, et retrouve le LEI d'une entrée à partir de sa raison sociale exacte dans son pays.
* [Attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain) ajoute ou associe le VASP qui possède un portefeuille de destination, et conserve le nom attribué comme alias.
* L'équipe Didit organise les entrées : identifiants de routage et inscriptions sur les réseaux qu'elle a vérifiés, contacts de conformité, alias, marques et leurs entités, doublons fusionnés et entrées masquées.

Les données qu'un client signale au sujet d'une contrepartie, comme un `originating_vasp` entrant ou un Travel Address, restent sur le transfert de ce client et ne modifient jamais une entrée partagée. Les entrées sont rapprochées d'abord par LEI, puis par nom exact ou alias, et enrichies plutôt que dupliquées.

### Le réseau Didit

Votre profil VASP est membre du réseau Didit, de sorte que les autres clients Didit le trouvent dans l'annuaire et dans le sélecteur de VASP de l'utilisateur final, uniquement si toutes les conditions suivantes sont remplies :

* votre application est en production (et non en sandbox) et la Travel Rule est activée ;
* `is_discoverable` vaut `true` et `legal_name` est renseigné ;
* `compliance_email` est renseigné et confirmé : envoyez la confirmation avec `POST /v3/travel-rule/settings/compliance-email/confirmation/` et ouvrez le lien reçu par e-mail. Modifier l'adresse nécessite une nouvelle confirmation ;
* Didit n'a pas marqué l'application comme une application de test, de démonstration ou interne.

Le rail `INTERNAL` effectue lui-même la livraison vers un portefeuille dont vous avez prouvé la propriété, comme décrit dans [Envoi d'un transfert](#envoyer-un-transfert) : il exige que votre application soit en production, que la Travel Rule soit activée et que `is_discoverable` soit vrai, et non le reste de la liste.

`GET /v3/travel-rule/settings/` renvoie `didit_network` : `listed`, `compliance_email_confirmed`, et dans `missing` chaque condition encore non remplie (`travel_rule_off`, `sandbox_application`, `not_discoverable`, `legal_name_missing`, `compliance_email_missing`, `compliance_email_unconfirmed`, `excluded_by_staff`, `internal_organization`).

### Identifiants de routage

Le routage sortant consulte une entrée de l'annuaire chaque fois que votre transfert désigne la contrepartie par `counterparty_vasp_lei` ou `counterparty_vasp_name` (un alias fonctionne aussi). Une entrée porte un identifiant par réseau sur lequel elle est joignable, un endpoint TRP vérifié et ses contacts de conformité, et `routes` indique lesquels de ces éléments votre application peut utiliser. Vous pouvez toujours contourner l'annuaire pour un seul transfert en transmettant les champs explicites décrits dans [Champs de résolution de la contrepartie](#champs-de-résolution-de-la-contrepartie).

## Diligence raisonnable sur la contrepartie

Avant que les fonds de vos clients n'atteignent un autre VASP, vous devez savoir qui est ce VASP et si vous l'acceptez. La diligence raisonnable sur une contrepartie comporte deux niveaux :

* **L'évaluation partagée.** Chaque entrée de l'annuaire porte un score de diligence raisonnable, son niveau de risque, un statut de réactivité et un rapport de contrôles essentiels. Didit les calcule à partir des données propres à la plateforme, de sorte que tous les clients voient les mêmes valeurs.
* **Vos propres dossiers.** Votre décision concernant le VASP, le filtrage des sanctions et de la presse négative le concernant, ainsi que les questionnaires de diligence raisonnable échangés avec lui, appartiennent uniquement à votre application. Aucun autre client ne les voit, et ils ne modifient jamais l'entrée partagée.

Vos propres dossiers fonctionnent aussi bien pour une entrée de l'annuaire que pour un membre du [réseau Didit](#le-réseau-didit) : identifiez-les à l'aide de l'`id` affiché par l'[annuaire](#annuaire).

| Endpoint | Fonction |
| - | - |
| `GET` / `PUT` / `DELETE /v3/travel-rule/vasps/{vasp_id}/decision/` | Lire, définir ou effacer votre [décision](#votre-décision-sur-une-contrepartie) concernant la contrepartie. |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/screening/` | Lire votre dernier [filtrage de la contrepartie](#filtrage-de-la-contrepartie), ou en lancer un. |
| `GET` / `POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/` | Lister vos [questionnaires de diligence raisonnable](#questionnaire-de-diligence-raisonnable) échangés avec la contrepartie, ou en envoyer un. |
| `GET /v3/travel-rule/transfers/?counterparty={vasp_id}` | Vos [transferts avec cette contrepartie](#transferts-avec-une-seule-contrepartie). |

### Score et niveau de risque

`dd_score` va de 0 à 100, et **plus la valeur est élevée, plus c'est sûr**. Chaque résultat l'indique dans `dd_score_semantic`, qui vaut toujours `higher_is_safer`. `risk_level` est la tranche dans laquelle se situe le score :

| `risk_level` | `dd_score` |
| - | - |
| `LOW` | 70 ou plus |
| `MEDIUM` | de 40 à 69 |
| `HIGH` | Inférieur à 40 |

Le score est la somme de sept composantes. `dd_score_breakdown` affiche chacune d'elles sous la forme `{points, max, reason, detail}` : `max` correspond au poids de la composante, `reason` est un code lisible par une machine que vous pouvez traduire, et `detail` est une explication en anglais d'une ligne.

| Composante | Poids | Mode de notation | Codes `reason` |
| - | - | - | - |
| `jurisdiction` | 25 | Score complet lorsque le pays de l'entrée ne figure pas dans la liste par défaut des juridictions à haut risque (`IRN`, `PRK`, `SYR`, `AFG`, `RUS`, `MMR`), aucun point lorsqu'il y figure, et la moitié des points lorsque le pays est inconnu. | `jurisdiction_not_high_risk`, `jurisdiction_high_risk`, `jurisdiction_unknown` |
| `verification` | 15 | Statut de vérification de l'entrée par Didit (`dd_status`) : score complet pour `COMPLETED`, la moitié pour `IN_PROGRESS`, un quart pour `NOT_STARTED`, aucun point pour `REJECTED`. | `dd_status_completed`, `dd_status_in_progress`, `dd_status_not_started`, `dd_status_rejected` |
| `protocol` | 15 | Score complet lorsqu'un canal de protocole natif atteint l'entrée (un endpoint TRP ou le canal `INTERNAL`), 40 pour cent lorsque seul le repli par e-mail est disponible, aucun point sans canal joignable. | `native_rail`, `email_only`, `no_rail` |
| `lei` | 10 | Score complet avec un LEI valide enregistré, aucun point sans LEI. | `lei_valid`, `lei_missing` |
| `history` | 10 | Part des échanges de l'entrée dont l'issue est déterminée qui ont réussi, la moitié des points en l'absence de tout échange. | `history`, `no_history` |
| `essentials` | 15 | Part des [contrôles essentiels](#contrôles-essentiels) décidés qui réussissent, la moitié des points tant qu'aucun n'est décidé. | `essential_checks`, `no_essential_checks` |
| `responsiveness` | 10 | Score complet pour `RESPONSIVE`, aucun point pour `UNRESPONSIVE`, la moitié des points pour `UNKNOWN`. Voir [Réactivité](#réactivité). | `responsive`, `unresponsive`, `responsiveness_unknown` |

Les points sont arrondis à une décimale, et le total à un nombre entier. `history` se base sur les mêmes requêtes que la [réactivité](#réactivité), sans fenêtre temporelle : un échange `COMPLETED` ou `FINISHED` est un succès, tandis qu'un échange `EXPIRED`, `COUNTERPARTY_MISMATCHED_DATA` ou `COUNTERPARTY_VASP_GENERAL_DECLINE` est un échec. Une annulation relève de l'initiative de l'expéditeur et n'est jamais comptabilisée.

Didit recalcule l'évaluation lorsqu'un échange avec l'entrée est terminé, achevé, expiré ou en discordance, ou refusé, lorsqu'il actualise ou gère l'entrée, et pour chaque entrée lors de l'actualisation hebdomadaire de l'annuaire. `dd_assessed_at` indique la date de sa dernière exécution. Une entrée qui n'a pas encore été évaluée n'a pas de `dd_assessed_at`, a un `dd_score` de `0` et un `risk_level` vide ; lisez donc `risk_level` plutôt que le score brut. Les membres du réseau Didit ne sont pas notés : leur `dd_score` vaut `null`, leur `risk_level` est vide et leur `dd_score_breakdown` est vide.

### Contrôles essentiels

`essential_checks` est un rapport succinct sur quatre contrôles que Didit effectue sur chaque entrée de l'annuaire, uniquement à partir des sources vérifiées de la plateforme, jamais à partir de données signalées par un client :

| `id` du contrôle | Réussite lorsque |
| - | - |
| `registration` | Un registre officiel répertorie le VASP : le registre intérimaire européen MiCA des prestataires de services sur crypto-actifs, l'annuaire public TRISA, ou le registre mondial LEI avec un LEI délivré. |
| `licence` | Le registre MiCA enregistre une autorisation du VASP qui n'a pas pris fin. |
| `domain` | Le domaine de l'entrée correspond à son endpoint TRP ou au site web indiqué par son registre. |
| `lei` | L'enregistrement LEI de l'entrée est actif et délivré, et sa raison sociale et son pays correspondent à ceux de l'entrée. |

Chaque contrôle de `checks` indique son `status` (`pass`, `fail`, ou `unknown` lorsqu'aucune source enregistrée ne permet de trancher), une `reason` lisible par une machine et un `detail` en anglais d'une ligne. `passed`, `failed`, `unknown` et `total` comptent les contrôles, `level` vaut `essential`, et `checked_at` indique le moment où le rapport a été établi. La composante `essentials` note la part des contrôles décidés qui réussissent : un contrôle `unknown` ne compte donc ni pour ni contre l'entrée. Le rapport est vide tant que l'actualisation hebdomadaire de l'annuaire n'a pas vérifié une première fois l'entrée, et il reste toujours vide pour un membre du réseau Didit.

### Réactivité

`responsiveness` indique si le VASP répond aux demandes Travel Rule :

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

`total` compte les requêtes que les clients de Didit ont envoyées au VASP au cours des 90 derniers jours et dont le résultat est définitif, et `answered` celles auxquelles il a répondu :

* **Comptabilisées :** les requêtes envoyées par un canal réseau ou protocolaire, ainsi que les requêtes envoyées par e-mail à une adresse partagée, c'est-à-dire l'adresse de conformité de l'entrée ou un contact de la plateforme.
* **Jamais comptabilisées :** une requête envoyée par e-mail à une adresse saisie par un client, ou à un contact privé d'un client. Cet échange reste dans l'historique de transferts du client et ne qualifie jamais le VASP aux yeux des autres.
* **Répondues :** `COMPLETED`, `FINISHED`, `COUNTERPARTY_VASP_GENERAL_DECLINE` ou `COUNTERPARTY_MISMATCHED_DATA`. Un refus ou une discordance constitue une réponse : le VASP a répondu.
* **Sans réponse :** `EXPIRED`.
* **Ni l'un ni l'autre :** tous les autres statuts. Une requête `CANCELLED` relève de l'initiative de l'expéditeur, `COUNTERPARTY_VASP_NOT_REACHABLE` peut être un échec du côté de l'expéditeur, et une requête encore en cours n'est pas encore décidée.

| `status` | Quand |
| - | - |
| `UNKNOWN` | Moins de 5 demandes ayant fait l'objet d'une décision sur la période, afin qu'une seule demande expirée ne classe jamais un VASP. |
| `UNRESPONSIVE` | Le VASP a répondu à moins de 10 pour cent de ses demandes tranchées. |
| `RESPONSIVE` | Dans les autres cas. |

Les membres du réseau Didit répondent au sein de Didit et affichent toujours `UNKNOWN` avec des compteurs à zéro. Prenez des mesures à l'encontre d'une contrepartie qui ne répond pas avec la [politique de non-réponse](#politiques-de-période-de-transition-et-d’absence-de-réponse) ou le [préréglage](#champs-de-règle-et-préréglages) `travel-rule-counterparty-unresponsive`.

### Votre décision sur une contrepartie

Enregistrez le verdict de votre équipe de conformité sur une contrepartie avec `PUT /v3/travel-rule/vasps/{vasp_id}/decision/` :

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

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

| `decision` | Signification | Préréglage qui l'applique |
| - | - | - |
| `APPROVED` | Votre équipe a approuvé la contrepartie. | Aucun |
| `RESTRICTED` | Les transferts vers elle peuvent être effectués après examen. | `travel-rule-counterparty-restricted` les met en attente d'examen. |
| `BLOCKED` | Votre équipe a refusé la contrepartie. | `travel-rule-counterparty-blocked` les refuse. |

* La décision est privée pour votre application : aucun autre client ne la voit, et elle ne modifie jamais l'entrée partagée.
* Une décision ne change rien à elle seule et ne modifie jamais l'acheminement d'un transfert. Elle s'applique à vos transactions par l'intermédiaire du [champ de règle](#champs-de-règle-et-préréglages) `counterparty_dd_status` : installez les préréglages, ou écrivez votre propre règle sur ce champ.
* Une décision sur une marque couvre chacune de ses [entités](#marques-et-leurs-entités) qui n'a pas de décision propre, et l'entité affiche la marque dans `inherited_from`. Une décision sur l'entité remplace celle de la marque pour cette entité.
* `GET` renvoie la décision applicable, ou `{"decision": null}`. `DELETE` supprime votre propre décision et renvoie celle qui s'applique ensuite : la décision de la marque pour une entité, sinon `null`. Il répond `404` lorsque votre application n'a aucune décision propre sur la contrepartie.
* `reason` est facultatif, limité à 255 caractères, et chaque `PUT` le remplace. `decided_by` est l'utilisateur de la console qui a pris ou modifié en dernier la décision, et il est vide lorsqu'une clé API l'a fait.

### Filtrage de la contrepartie

Le filtrage de la contrepartie vérifie le VASP lui-même, en tant qu'entreprise, contre les listes de sanctions et la presse négative. Il est **facultatif et facturé** : activez-le avec `counterparty_screening_enabled` dans vos [paramètres](#configuration). Tant qu'il est désactivé, rien n'est filtré ni facturé.

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

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

* **Facturation.** Chaque nouveau filtrage coûte le [prix](https://docs.didit.me/fr/getting-started/pricing.md) du Filtrage AML pour entreprises de votre organisation. `GET /v3/travel-rule/vasps/{vasp_id}/screening/` renvoie ce `price`, `fresh_days` et si le filtrage est `enabled`, sans frais. Un filtrage datant de moins de 30 jours est considéré comme récent : `POST` le renvoie à nouveau avec `200` et `outcome: reused` sans rien facturer, sauf si vous envoyez `refresh: true`. Un nouveau filtrage répond `201` avec `outcome: screened`. Les applications sandbox ne sont jamais filtrées ni facturées.
* **Filtrage automatique.** Lorsque le filtrage est activé, Didit filtre aussi la contrepartie vers laquelle est effectué un échange sortant dès le début de celui-ci, avant que vos règles n'évaluent la transaction, avec la même réutilisation et la même facturation. Un refus ou un échec à cette étape ne fait jamais échouer le transfert.
* **Résultat.** `status` vaut `Approved` lorsqu'aucune correspondance possible ne demande d'attention, et `In Review` ou `Declined` pour une correspondance possible avec des sanctions ou de la presse négative. `flagged` vaut `true` tant qu'un filtrage récent est `In Review` ou `Declined`.
* **Effet.** Tant que votre dernier filtrage de la contrepartie est signalé et que le filtrage reste activé, `counterparty_risk_level` vaut `HIGH` dans vos règles, de sorte que le préréglage `travel-rule-counterparty-high-risk`, une fois installé, met en attente ses transferts. Le `risk_level` partagé du répertoire ne change pas.
* **Confidentialité.** Le filtrage est privé pour votre application : aucun autre client ne le voit, et il ne modifie jamais l'entrée partagée. Le répertoire affiche votre dernier filtrage sous `entity_screening`.
* **Refus.** `409` lorsque le filtrage est désactivé, pour une application sandbox, ou lorsqu'un filtrage de la même contrepartie est déjà en cours. `403` avec `code: insufficient_credits` lorsque votre solde ne couvre pas le prix. `502` lorsque le filtrage n'a pas pu être terminé. Aucun de ces cas ne facture quoi que ce soit.

### Questionnaire de diligence raisonnable

Demandez à une contrepartie ses réponses au questionnaire de diligence raisonnable standard de Didit. `POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/` l'envoie par e-mail à une adresse de conformité enregistrée pour la contrepartie, avec un lien à usage unique vers une page de réponse :

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

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

* **Destinataire.** `sent_to` doit être une adresse de conformité enregistrée : pour une entrée du répertoire, son adresse de conformité ou un contact que votre application voit dans `contacts` ; pour un membre du réseau Didit, son e-mail de conformité confirmé. Omettez-le pour utiliser l'adresse à laquelle est envoyé un e-mail Travel Rule à la contrepartie : d'abord votre propre contact, puis un contact de la plateforme, puis l'adresse de conformité de l'entrée. Didit n'envoie jamais le questionnaire ailleurs, et répond `400` lorsque la contrepartie n'a aucune adresse de conformité enregistrée.
* **Réponses.** Le lien ouvre `/v3/travel-rule/questionnaires/{token}/`, une page destinée à une personne de la contrepartie, qui ne nécessite ni compte ni clé API. Elle répond à chaque question, jusqu'à 2 000 caractères chacune (`N/A` lorsqu'une question ne s'applique pas), et soumet une seule fois. Une contrepartie peut aussi envoyer `{"answers": {"<question id>": "<answer>"}}` au même lien. Un lien déjà utilisé, remplacé ou dépassant sa date limite affiche une page d'expiration à la place. L'e-mail et la page sont traduits ; les questions elles-mêmes restent en anglais, afin que leur sens ne change jamais d'une langue à l'autre.
* **Délais.** La contrepartie dispose de 30 jours pour répondre (`expires_at`). Les réponses restent valables pendant 365 jours (`valid_until`), et `current` vaut `true` jusque-là. Un questionnaire resté sans réponse après sa date limite, et des réponses au-delà de leur validité, passent à `EXPIRED`.
* **Un seul questionnaire ouvert par contrepartie.** Un nouvel envoi remplace votre questionnaire précédent resté sans réponse pour la même contrepartie : il passe à `EXPIRED` et son lien cesse de fonctionner. Un seul e-mail par minute peut être envoyé à une adresse ; un envoi plus rapide répond `429`.
* **Lecture des réponses.** `GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/` liste vos questionnaires avec la contrepartie, du plus récent au plus ancien, avec `answers` indexées par identifiant de question une fois répondues. Le répertoire les affiche sous `questionnaires`. Elles sont privées pour votre application.

Le formulaire, version `1`, pose ces douze questions :

| Identifiant de la question | Question |
| - | - |
| `legal_name` | Dénomination juridique de l'entité et juridiction de constitution. |
| `licensing` | Licences ou enregistrements détenus pour les services d'actifs virtuels : régulateur, numéro de licence et statut dans chaque juridiction. |
| `jurisdictions` | Juridictions dans lesquelles vous exercez, et celles dans lesquelles vous êtes exclu ou soumis à restrictions. |
| `aml_program` | Résumé de votre programme AML/CFT : politiques, procédures et date du dernier audit ou examen indépendant. |
| `compliance_officer` | Nom et rôle de votre responsable de la conformité, et les moyens de le contacter. |
| `sanctions_screening` | Méthode de filtrage de vos clients et contreparties au regard des sanctions et de la presse négative : fournisseur ou listes, fréquence et escalade. |
| `transaction_monitoring` | Méthode de surveillance des transactions à la recherche d'activités suspectes : outils, seuils et pratiques de déclaration. |
| `travel_rule` | Méthode de mise en conformité avec la Travel Rule : protocoles et réseaux utilisés, et délai de réponse habituel à une demande. |
| `custody` | Dispositifs de conservation des actifs clients : dépositaire qualifié, assurance et séparation des actifs. |
| `ownership` | Bénéficiaires effectifs détenant 25 pour cent ou plus, et toute entité mère ou entité faîtière. |
| `record_keeping` | Pratique de conservation des enregistrements et durée de conservation des données Travel Rule et des données de transaction. |
| `data_protection` | Méthode de protection des données personnelles partagées à des fins de conformité, et lieu de leur stockage. |

### Transferts avec une seule contrepartie

[`GET /v3/travel-rule/transfers/?counterparty={vasp_id}`](https://docs.didit.me/fr/management-api/travel-rule/list-transfers) liste les transferts Travel Rule de votre application avec une seule contrepartie, du plus récent au plus ancien, selon l'`id` affiché par l'annuaire : vos transferts dont la contrepartie est cette entrée d'annuaire, ou vos échanges `INTERNAL` avec ce membre du réseau Didit. Une valeur qui n'est pas un identifiant valide renvoie `400`.
Chaque ligne affiche `reason` juste après `status` : la [`reason`](#pourquoi-un-transfert-est-bloqué) propre au transfert, afin que la liste vous permette de distinguer un e-mail de conformité rejeté d'une contrepartie sans itinéraire, sans ouvrir chaque transfert.

### Politiques de période de transition et d'absence de réponse

Lorsqu'un transfert expire (le délai `confirmation_timeout_hours` est écoulé alors qu'il était `AWAITING_COUNTERPARTY`), `timeout_outcome` détermine le sort de sa transaction. Deux paramètres vous permettent de traiter différemment deux motifs courants de délai dépassé :

| Paramètre | S'applique lorsque la contrepartie est une entrée d'annuaire qui |
| - | - |
| `sunrise_policy` | Se trouve dans une juridiction listée dans `sunrise_jurisdictions` : un pays qui n'applique pas encore la Travel Rule, de sorte que le VASP n'est pas tenu de répondre. |
| `non_responsive_policy` | Possède `responsiveness.status` égal à `UNRESPONSIVE`. |

Les deux acceptent `HOLD`, `REJECT` ou `PROCEED`, avec la même signification que pour `timeout_outcome`, ou une chaîne vide pour suivre `timeout_outcome`. Les deux sont vides par défaut, de sorte que rien ne change tant que vous n'en définissez pas un :

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

La première de ces politiques qui s'applique détermine le résultat :

1. `sunrise_policy`, lorsqu'elle est définie et que la juridiction de la contrepartie figure dans `sunrise_jurisdictions`.
2. `non_responsive_policy`, lorsqu'elle est définie et que la contrepartie est `UNRESPONSIVE`.
3. `timeout_outcome` dans tous les autres cas, y compris pour un transfert vers un membre du réseau Didit ou vers une contrepartie sans entrée d'annuaire.

`sunrise_jurisdictions` est en lecture seule et vaut actuellement `["CO", "KH", "VN"]`. Seuls ces pays sont considérés comme étant en période de transition : toute autre juridiction, ainsi qu'une juridiction vide ou inconnue, suit `timeout_outcome`, de sorte qu'une contrepartie ne bénéficie jamais de votre politique de période de transition par erreur. La chronologie du transfert expiré indique le résultat appliqué, par exemple `sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet)`. Vos règles continuent de s'exécuter après l'expiration, de sorte qu'une règle telle que `tr_eu_expired_review` du TFR européen peut toujours faire passer la transaction en examen sous `PROCEED`.

### Champs de règle et préréglages

Quatre champs de [règle](https://docs.didit.me/fr/transaction-monitoring/rules.md) contiennent ce que votre application sait de la contrepartie avec laquelle l'échange Travel Rule d'une transaction a été réalisé : son entrée d'annuaire ou, pour un échange `INTERNAL`, le client Didit qui l'a reçu. Chaque champ est vide lorsque l'échange n'a atteint aucune contrepartie connue.

| Champ | Valeur |
| - | - |
| `counterparty_dd_status` | Votre [décision](#votre-décision-sur-une-contrepartie) : `APPROVED`, `RESTRICTED` ou `BLOCKED`, pour une entité qui suit celle de sa marque. Vide lorsque vous n'en avez pris aucune. Jamais la décision d'un autre client. |
| `counterparty_risk_level` | Le `risk_level` de l'entrée (`LOW`, `MEDIUM` ou `HIGH`), ou `HIGH` tant que votre propre [filtrage](#filtrage-de-la-contrepartie) de la contrepartie est signalé. Vide pour une entrée pas encore évaluée, et pour un membre du réseau Didit sauf si votre filtrage de ce membre est signalé. |
| `counterparty_jurisdiction` | Le pays de la contrepartie, issu de l'entrée d'annuaire ou du profil du membre du réseau Didit. Les codes ISO-2 et ISO-3 sont tous deux comparés, comme pour les autres champs de pays. |
| `counterparty_responsiveness` | Le `responsiveness.status` de l'entrée : `RESPONSIVE`, `UNRESPONSIVE` ou `UNKNOWN`. Vide pour un membre du réseau Didit. |

Cinq préréglages du bundle `travel_rule` agissent sur ces champs. Installez-les depuis **Transactions → Rules → Library** dans la Business Console, ou avec l'[API de la bibliothèque de règles](https://docs.didit.me/fr/management-api/transactions/rules-install.md) :

| `library_key` | Déclencheur | Action par défaut |
| - | - | - |
| `travel-rule-counterparty-blocked` | `counterparty_dd_status` vaut `BLOCKED` | +90 au score, `DECLINED` |
| `travel-rule-counterparty-restricted` | `counterparty_dd_status` vaut `RESTRICTED` | +50 au score, `IN_REVIEW` |
| `travel-rule-counterparty-high-risk` | `counterparty_risk_level` vaut `HIGH` | +40 au score, `IN_REVIEW` |
| `travel-rule-counterparty-high-risk-jurisdiction` | `counterparty_jurisdiction` figure dans la liste par défaut des juridictions à haut risque (`IRN`, `PRK`, `SYR`, `AFG`, `RUS`, `MMR`) | +60 au score, `IN_REVIEW` |
| `travel-rule-counterparty-unresponsive` | `counterparty_responsiveness` vaut `UNRESPONSIVE` | +30 au score, `IN_REVIEW` |

Les cinq préréglages ne s'appliquent qu'aux transactions Travel Rule.

## Webhooks

Abonnez une destination à `travel_rule.status.updated` pour être notifié à chaque transition de statut de chaque transfert de votre application, sortant comme entrant, y compris les tronçons sur le rail `INTERNAL`.
Le routage s'exécute après le retour de la création de la transaction. Des résultats tels que `COUNTERPARTY_VASP_NOT_FOUND`, `COUNTERPARTY_VASP_NOT_REACHABLE`, ou un échange `INTERNAL` réglé immédiatement, vous parviennent donc par cet événement et non dans la réponse à la création.

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

`rail` reste vide tant qu'aucun rail n'est sélectionné.
Pour une même transaction, Didit envoie `travel_rule.status.updated` après `transaction.created` : lorsque Didit crée la transaction (votre `POST /v3/transactions/`, un enregistrement sunrise, une demande `TRP` entrante, ou le tronçon entrant sur le rail `INTERNAL`), il envoie d'abord `transaction.created`.
Chaque événement est livré et réessayé de manière indépendante. Un `transaction.created` qui a nécessité une nouvelle tentative peut donc arriver après un événement de statut. Basez votre gestionnaire sur `transaction_id`, et lisez la transaction lorsqu'un événement de statut en désigne une que vous n'avez pas encore vue.
Un transfert envoyé via `TRUST` ne reçoit aucun événement `COMPLETED` : TRUST ne renvoie rien que Didit puisse lire. Sauf si l'envoi échoue ou si vous l'annulez, son dernier événement est donc `EXPIRED`. Via `GTR`, `VERIFYVASP` et `SYGNA`, l'événement suit la réponse que Didit lit sur le réseau.
Les applications sandbox reçoivent aussi cet événement, avec `"environment": "sandbox"`. Un transfert auquel le [Didit Test VASP](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) a répondu porte `rail: "TEST_VASP"`, `test_vasp: true` et `test_vasp_scenario`.

Consultez le [guide des webhooks](https://docs.didit.me/fr/integration/webhooks.md#types-d’événements) pour le catalogue complet des événements, la vérification des signatures et la politique de nouvelles tentatives.

## Tarifs

Les frais Travel Rule s'élèvent à **\$0.02 par échange sortant lancé**, facturés une seule fois à la création de la transaction, quel que soit le nombre de transitions de statut que l'échange traverse ensuite. Un `resend` ne les facture jamais à nouveau.

* Ils s'appliquent à une transaction `travelRule` sortante sur une application où Travel Rule est activé, dont le montant est égal ou supérieur à votre `threshold_amount`, de sorte que Didit lance pour elle un échange géré. Un transfert vers un portefeuille auto-hébergé, ou vers le portefeuille vérifié de votre utilisateur, compte aussi : la preuve de propriété constitue son échange.
* Toute autre transaction sortante qui contient des données Travel Rule est facturée comme Transaction Screening standard, inclus dans votre forfait jusqu'à son [quota mensuel de transactions](https://docs.didit.me/fr/getting-started/pricing.md#surveillance-des-transactions-kyt), puis à \$0.001 au-delà. Sont concernés : un transfert sous votre seuil, un paiement Lightning auquel la Travel Rule ne s'applique pas, et un transfert enregistré passivement lorsque Travel Rule est désactivé. Une transaction est facturée sur l'une des deux lignes, jamais sur les deux.
* Les transactions `travelRule` entrantes ne donnent lieu à aucuns frais Travel Rule ni de surveillance : c'est le cas du tronçon entrant que Didit crée sur l'application du bénéficiaire via le rail `INTERNAL`, des demandes `TRP` entrantes et des [enregistrements après dépôt](#enregistrement-a-posteriori-sunrise).
* Une application qui a rejoint Travel Rule gratuitement ne paie aucun des deux frais sur les transactions `travelRule` sortantes tant qu'elle n'active pas la facturation sortante avec `POST /v3/travel-rule/workspace/billing/`. La réception reste gratuite.
* Les applications sandbox ne sont jamais facturées.
* Les frais Travel Rule sont affichés à $0.02, et Transaction Screening à $0.001 au-delà de votre quota. Chaque transaction est facturée au prix de la ligne qu'elle utilise.
* Si le filtrage crypto AML/portefeuille s'exécute aussi sur la même transaction (par exemple un dépôt entrant avec `currency_kind: "crypto"` et la surveillance activée), il est facturé séparément au tarif standard Wallet Verification & Screening. Il n'est pas inclus dans les frais Travel Rule.
* Le [filtrage de la contrepartie](#filtrage-de-la-contrepartie) est optionnel et facturé séparément, au prix Company AML Screening par filtrage. Un filtrage de moins de 30 jours est réutilisé sans frais. Les décisions, les questionnaires et l'évaluation de vigilance de l'annuaire ne coûtent rien.

### Tarifs de l'attribution

[L'attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain) nécessite une seule recherche sur le portefeuille auprès du fournisseur d'analyse, et vous obtenez toujours la valeur complète de cette recherche :

* **La surveillance crypto a déjà filtré le portefeuille de cette transaction** : le résultat enregistré est réutilisé. Un appel d'analyse, une seule facturation Wallet Verification & Screening (celle déjà effectuée par la surveillance crypto). L'attribution n'ajoute rien.
* **La surveillance crypto n'a pas été exécutée** : Didit effectue une seule fois l'appel d'analyse requis et transmet sa valeur complète. La transaction est facturée au tarif standard Wallet Verification & Screening (\$0.15 avec l'analyse gérée par Didit) et reçoit en retour l'intégralité du résultat du filtrage crypto (score de risque, catégorie de risque du portefeuille, instantané du réseau), et pas seulement le nom du VASP. La ligne de consommation porte le déclencheur `travel_rule_attribution`, afin que la facturation soit explicable.
* **Votre propre clé d'analyse** : les mêmes frais de plateforme que pour la surveillance crypto avec votre propre clé s'appliquent ici (\$0.02 par appel). Votre fournisseur vous facture directement la recherche sous-jacente.
* **Les recherches en échec ne sont jamais facturées**. Les renvois réutilisent l'attribution enregistrée, sans nouvel appel ni nouvelle facturation, et les applications sandbox ne sont jamais facturées.

Un transfert sortant qui a nécessité une attribution coûte donc \$0.02 (Travel Rule) plus une facturation Wallet Verification & Screening. Lorsque les deux produits sont activés, l'appel d'analyse et sa facturation n'ont lieu qu'une seule fois.

## Ensemble de règles EU TFR

Quatre règles prédéfinies sont incluses dans l'ensemble `travel_rule_eu_tfr`, conçues spécifiquement pour les obligations du règlement EU TFR.
Comme tout autre [ensemble de règles prédéfinies](https://docs.didit.me/fr/transaction-monitoring/rules-library.md), activez-le depuis la Business Console sous **Transactions → Rules → Library**.

| Règle (`library_key`) | Déclencheur | Action |
| - | - | - |
| `tr_eu_mismatch_hold` | Le statut du transfert est `COUNTERPARTY_MISMATCHED_DATA` | Score `+50`, transaction placée dans l'état `IN_REVIEW` |
| `tr_eu_unverified_selfhosted_review` | Le statut du transfert est `UNCONFIRMED_OWNERSHIP` et le montant dans la devise par défaut est au moins `1000` | Transaction placée dans l'état `IN_REVIEW` |
| `tr_eu_vasp_not_found_score` | Le statut du transfert est `COUNTERPARTY_VASP_NOT_FOUND` | Score `+25`, transaction étiquetée `travel-rule-sunrise` |
| `tr_eu_expired_review` | Le statut du transfert est `EXPIRED` | Transaction placée dans l'état `IN_REVIEW` |

Ces règles se déclenchent à chaque transition pertinente : à la création du transfert, lors de la confirmation de propriété, lors de la réponse à la récupération par e-mail et via le processus de traitement des expirations. Ainsi, un transfert qui ne se résout jamais reste signalé pour examen de conformité au lieu de se régler sans bruit.

## Pourquoi cela fonctionne mieux sur une seule plateforme

La conformité Travel Rule est un problème d'identité déguisé en problème de paiement, et Didit détient déjà l'identité :

* **Données vérifiées à portée de main.** La personne concernée par chaque transfert est reliée par `vendor_data` au profil KYC que Didit a vérifié lors de l'onboarding (OCR du document, détection du vivant, correspondance faciale). Vous pouvez donc construire `originator_data` à partir de champs vérifiés, sur la même plateforme.
  Pour un utilisateur dont la vérification KYC est approuvée, Didit transmet l'identité vérifiée et non les valeurs que vous avez saisies (voir [Données vérifiées et provenance](#données-vérifiées-et-provenance)).
* **Biométrie renforcée pour les transferts à risque.** Une [règle](https://docs.didit.me/fr/transaction-monitoring/rules.md) peut mettre en attente tout transfert de montant élevé jusqu'à ce que l'utilisateur réussisse une revérification biométrique par rapport au visage capturé lors de l'onboarding. C'est la meilleure réponse possible à la prise de contrôle de compte au moment du retrait. Voir l'[exemple détaillé](https://docs.didit.me/fr/transaction-monitoring/sdk-transaction-submission.md#exemple-de-renforcement-biométrique-pour-un-retrait-d’un-montant-élevé).
* **Un seul graphe d'appareils.** Les transferts soumis via le SDK portent la même analyse de l'appareil que les sessions KYC. Ainsi, « nouvel appareil + montant élevé + bénéficiaire pour la première fois » constitue une seule règle, et non une jointure dans un entrepôt de données.
* **Filtrage, règles, dossiers et échange au même endroit.** Le filtrage des portefeuilles qui identifie le VASP de la contrepartie est le même filtrage que vos règles et vos analystes consultent : une seule facturation, une seule console, un seul journal d'audit.
* **Tarifs publiés.** [\$0.02 par échange sortant](#tarifs), sans paliers de volume, sans appel commercial.

## FAQ

### Comment fonctionne l'échange

> [accordion] Didit exécute-t-il lui-même l'échange VASP à VASP, ou se contente-t-il d'enregistrer les données Travel Rule après qu'un autre réseau a terminé la poignée de main ?
>
> Didit exécute l'échange lui-même.
> Lorsque vous soumettez un transfert `travelRule`, Didit effectue une découverte de contrepartie en temps réel, envoie les données de l'initiateur et du bénéficiaire au VASP destinataire via les canaux qui les transportent, et ne marque le transfert `COMPLETED` qu'une fois que la contrepartie le confirme : il s'agit d'une poignée de main gérée, et non d'un journal post-règlement.
> Une réponse de la contrepartie est traitée sur tous les canaux sauf `TRUST`, qui ne fait qu'envoyer ; un transfert passant par lui expire donc au lieu d'aboutir. Sur `GTR`, `VERIFYVASP` et `SYGNA`, Didit lit la réponse dans la réponse du réseau ou l'interroge toutes les 5 minutes, et aucun échange sur ces canaux n'a encore abouti sur un réseau en production ; sur `TRP`, une réponse que la contrepartie ne donne pas immédiatement nécessite l'hôte TRP public de Didit, qui n'est pas encore en service (voir [Réseaux Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md)).
> Didit conserve aussi l'enregistrement complet et le statut de conformité de chaque échange (un [cycle de vie](#cycle-de-vie) à 13 états, des [webhooks](#webhooks) et une piste d'audit).
> Si vous préférez gérer Travel Rule via votre propre processus, Didit peut à la place enregistrer les données de façon passive comme votre système de référence unique (voir la question sur l'enregistrement passif ci-dessous).
> Les deux modes sont disponibles : l'échange actif s'exécute dès que vous définissez `is_enabled: true` dans les [paramètres](#configuration) (la valeur par défaut est `false`) ; jusque-là, Didit enregistre les données de façon passive.

> [accordion] L'échange est-il natif chez Didit, ou dépend-il d'un fournisseur d'interopérabilité externe (Notabene, Sygna, VerifyVASP, TRP) ?
>
> Il est natif. Didit exécute sa propre implémentation du Travel Rule Protocol (TRP), servie sur `trp.didit.me` une fois que cet hôte public sera en service (il ne répond pas encore, donc les contreparties ne peuvent pas vous joindre via TRP aujourd'hui), et implémente directement les protocoles des réseaux du secteur - `GTR` (Global Travel Rule), `TRUST`, `VerifyVASP` et `Sygna Bridge` (pris en charge nativement, y compris l'IVMS 101 chiffré en ECIES de Sygna et les demandes d'autorisation signées). Didit ne revend pas une couche d'interopérabilité tierce unique. Atteindre une contrepartie présente sur un réseau d'adhésion spécifique passe nécessairement par ce réseau - il n'existe pas de canal universel unique dans le Travel Rule - mais la découverte, la poignée de main, la vérification et la tenue des registres sont tous exécutés par Didit.
> Aujourd'hui, chaque canal de réseau nécessite votre propre adhésion, et `TRUST` ne fait qu'envoyer : voir [Vue d'ensemble](#vue-d’ensemble).

> [accordion] Quels réseaux et protocoles Travel Rule Didit prend-il en charge ?
>
> Implémentés nativement, essayés dans un ordre de priorité strict : `INTERNAL` (les deux VASP sur Didit), `TRP` (OpenVASP Travel Rule Protocol), `GTR`, `TRUST`, `VerifyVASP`, `Sygna Bridge`, et un repli `EMAIL` pour les contreparties sans outillage.
> Les canaux de réseau nécessitent aujourd'hui votre propre adhésion, `TRUST` ne fait qu'envoyer, et la réception via un réseau n'est pas encore activée : voir [Réseaux Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md).
> CODE, TRISA, Notabene et Veriscope apparaissent dans la Marketplace de Didit sous forme de vignettes de demande d'accès, sans intégration pour l'instant.
> Voir [Comment Didit achemine un échange](https://docs.didit.me/fr/transaction-monitoring/travel-rule-interoperability.md#comment-didit-achemine-une-plateforme-d’échange) pour la table de routage complète, et [Réseaux Travel Rule](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md) pour savoir quelles contreparties chacun permet d'atteindre aujourd'hui.

> [accordion] Quel réseau dois-je utiliser, et ai-je besoin de mes propres adhésions ?
>
> `TRP` est la base que nous recommandons : il ne nécessite aucune adhésion partagée - partager votre [Travel Address](#travel-addresses) permet à tout VASP qui parle TRP de vous joindre - et lorsqu'une contrepartie n'est connue que par son adresse, l'[attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain) ne l'achemine via TRP que si l'entrée d'annuaire attribuée possède déjà un endpoint TRP.
> En sortie, Didit envoie via ce canal les actifs listés sous [identifiants d'actifs TRP](#identifiants-d’actifs-trp), et une contrepartie qui ne répond pas immédiatement nécessite l'hôte TRP public de Didit, qui n'est pas encore en service.
> Pour les réseaux d'adhésion, choisissez selon l'endroit où opèrent vos contreparties : GTR offre une large couverture mondiale, TRUST est orienté vers les VASP américains, et VerifyVASP (qui exploite désormais aussi Sygna Bridge) a une forte couverture en Asie.
> Oui, aujourd'hui vous devez disposer de votre propre adhésion : Didit ne détient encore aucune adhésion de plateforme sur ces réseaux et est en cours d'onboarding en tant que fournisseur.
> Connectez votre adhésion dans **Marketplace** ; vous n'êtes pas limité à un seul réseau.
> N'oubliez pas que `TRUST` ne fait qu'envoyer, de sorte qu'un transfert passant par lui n'aboutit pas, et qu'aucun échange sur un réseau n'a encore abouti sur un réseau en production.

> [accordion] Comment Didit trouve-t-il le VASP de la contrepartie lorsque je n'ai qu'une adresse ?
>
> Via une chaîne de résolution : un autre client Didit ayant prouvé le contrôle du portefeuille (le canal `INTERNAL`), votre propre [carnet d'adresses](#carnet-d’adresses-de-portefeuilles) pour une Travel Address enregistrée, puis les canaux de protocole et de réseau, puis l'[attribution par analyse blockchain](#attribution-par-analyse-de-la-blockchain).
> Si le transfert ne porte qu'un portefeuille de destination, Didit le résout avec des données d'analyse blockchain pour attribuer l'échange ou le service de conservation de la contrepartie et le répertorie dans l'[annuaire des VASP](#annuaire).
> Le transfert n'est alors routé automatiquement que si cette entrée d'annuaire dispose déjà d'un itinéraire, comme un endpoint `TRP` ; sinon il se termine avec `COUNTERPARTY_VASP_NOT_FOUND`, avec le nom de la contrepartie enregistré, et vous pouvez le renvoyer une fois qu'un itinéraire existe, par exemple après avoir enregistré le `travel_address` de la contrepartie dans votre entrée de carnet d'adresses pour ce portefeuille (pour les actifs listés sous [identifiants d'actifs TRP](#identifiants-d’actifs-trp)).

### Tarifs et licences

> [accordion] Combien coûte Travel Rule, et est-il inclus dans mon forfait KYC ou de vérification ?
>
> Travel Rule est un produit distinct, facturé à l'usage.
> Il n'est pas inclus dans la vérification d'identité.
> Il coûte \*\*$0.02 par échange sortant lancé** ([tarifs publiés](#tarifs), sans niveaux de volume ni appel commercial).
>         Lorsque le routage nécessite une recherche d'analyse blockchain pour identifier le VASP de la contrepartie, cette recherche est facturée une fois au [tarif d'attribution](#tarifs-de-l’attribution) : à partir de $0.15 en mode géré, ou \$0.02 avec une clé de fournisseur existante.
> Elle n'est pas refacturée si la surveillance crypto a déjà effectué le filtrage du même portefeuille.
> Un transfert en dessous de votre seuil, ou un transfert enregistré alors que Travel Rule est désactivé, est facturé comme filtrage des transactions ; les transferts entrants, comme les transactions que Didit crée côté bénéficiaire, ne sont pas soumis aux frais Travel Rule.

> [accordion] Serai-je facturé deux fois lorsque Travel Rule et la surveillance crypto fonctionnent tous deux ?
>
> Non.
> Une transaction `travelRule` est facturée sur une seule ligne : les frais Travel Rule (\$0.02) lorsqu'elle lance un échange sortant, sinon le filtrage des transactions standard. La recherche d'analyse blockchain est facturée une fois : si la surveillance crypto a déjà effectué le filtrage du portefeuille de destination de la transaction, l'[attribution](#attribution-par-analyse-de-la-blockchain) réutilise ce résultat stocké sans coût supplémentaire.
> Les transactions entrantes créées par Didit côté bénéficiaire ne sont jamais soumises aux frais Travel Rule.

> [accordion] Didit peut-il fournir une documentation ou une déclaration de capacités pour mon dossier de licence VASP ou auprès du régulateur ?
>
> Oui. La surveillance KYT et des transactions de Didit, le filtrage AML et des sanctions, l'analyse de risque des portefeuilles par analyse blockchain et les capacités Travel Rule sont toutes documentées publiquement sur docs.didit.me, et vous êtes libre de citer ces pages dans un dossier réglementaire. Pour une déclaration formelle de capacités du produit adaptée à un dépôt précis - par exemple une demande de licence VASP au titre de l'AIFC/AFSA ou de MiCA, ou une autre demande de licence VASP - contactez votre équipe de compte Didit avec le format exigé par votre régulateur.

### Opérations et données

> [accordion] Que se passe-t-il pour les transferts inférieurs à mon seuil ?
>
> Ils aboutissent immédiatement à `NOT_APPLICABLE` : aucun rail n'est sollicité et aucune donnée d'identité ne quitte Didit.
> La transaction est facturée comme un filtrage standard des transactions, et non au tarif Travel Rule.
> Didit compare le seuil au montant converti dans la devise par défaut de votre application.
> Si Didit ne peut pas convertir le montant, le seuil n'est pas appliqué et le transfert est échangé.
> Le règlement TFR de l'UE ne prévoit aucun minimum : les CASP de l'UE doivent donc conserver `threshold_amount` à `0.00`.

> [accordion] Que se passe-t-il si la contrepartie ne répond jamais ?
>
> Le transfert reste en `AWAITING_COUNTERPARTY` jusqu'à l'expiration de `confirmation_timeout_hours`. Un processus automatique le fait alors passer à `EXPIRED` et applique votre `timeout_outcome` à la transaction sous-jacente : `HOLD` pour examen, `REJECT` pour le refuser, ou `PROCEED` pour la laisser inchangée. La règle prédéfinie `tr_eu_expired_review` signale en outre chaque expiration à vos analystes. Une contrepartie qui ne prend jamais en charge le message est un cas différent : Didit réessaie, et après la sixième tentative échouée, le transfert passe à `COUNTERPARTY_VASP_NOT_REACHABLE` (voir [Livraison et nouvelles tentatives](#livraison-et-nouvelles-tentatives)).

> [accordion] Ma contrepartie ne dispose d'aucun outil Travel Rule. Puis-je tout de même être en conformité ?
>
> Oui : c'est le rail `EMAIL`. Le contact conformité de la contrepartie reçoit un lien de récupération à usage unique et peut confirmer ou refuser l'échange depuis une page hébergée, sans compte Didit, sans clé API et sans intégration. Pour les dépôts déjà réglés avant qu'un échange ne soit possible, utilisez l'[inscription sunrise après le dépôt](#enregistrement-a-posteriori-sunrise).

> [accordion] Comment fonctionnent les retraits vers le portefeuille propre de l'utilisateur ?
>
> Déclarez la destination comme auto-hébergée (`payment_method.method_type: "unhosted_wallet"`). Didit ne passe pas par le routage VASP et recueille plutôt la preuve que votre client contrôle le portefeuille, par signature de message, test de Satoshi, capture d'écran ou auto-déclaration, via le [widget de vérification de propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md). Il s'agit de l'obligation du TFR de l'UE relative aux portefeuilles auto-hébergés, sous forme d'API.

> [accordion] Les données personnelles de la contrepartie sont-elles protégées pendant l'échange ?
>
> Oui.
> Tant que la propriété du portefeuille n'est pas confirmée de votre côté, les données personnelles de la contrepartie d'un transfert entrant sont masquées, dans l'objet du transfert comme dans la partie contrepartie de la transaction (voir [Données de contrepartie masquées](#données-de-la-contrepartie-masquées)).
> Les pages de récupération par e-mail n'affichent que le résumé du transfert (nom du VASP donneur d'ordre, montant, portefeuille de destination), jamais les données des personnes.
> Les échanges sur les rails s'effectuent en HTTPS, et les rails de réseau (`GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`) chiffrent en outre le payload IVMS avec la clé publique du VASP destinataire.

> [accordion] Je gère déjà la conformité Travel Rule avec mon propre processus. Puis-je simplement l'enregistrer ?
>
> Oui. Lorsque Travel Rule est désactivé, tout `travel_rule_details` que vous envoyez (y compris un `status` fourni par l'appelant, actuel ou hérité) est enregistré passivement sur la transaction : aucun rail n'est sollicité et rien n'est négocié. Utilisez cette option pour compléter un historique ou faire tourner votre propre processus tout en conservant un seul système de référence.

> [accordion] Comment tester avant la mise en production ?
>
> Utilisez une application sandbox avec Travel Rule activé. Le sandbox ignore les règles, la facturation et les webhooks `transaction.created` et `transaction.status.updated`. Chaque échange **sortant** est dirigé vers le [VASP de test de Didit](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md) sur le rail `TEST_VASP`, qui répond au sein de Didit selon le scénario choisi : acceptation, refus, non-concordance du nom, injoignable, délai dépassé ou auto-hébergé. Vous recevez des webhooks `travel_rule.status.updated` marqués `environment: sandbox`, et aucun message n'atteint une véritable contrepartie.
> Pour les transferts **entrants**, faites envoyer par le VASP de test un transfert avec [Déclencher un transfert entrant de test du VASP de test](https://docs.didit.me/fr/management-api/travel-rule/trigger-test-vasp-inbound.md), ou enregistrez-en un avec `POST /v3/travel-rule/inbound/`. Dans les deux cas, la décision est prise à partir de votre carnet d'adresses de portefeuilles : `ON_HOLD`, `UNCONFIRMED_OWNERSHIP`, `COUNTERPARTY_MISMATCHED_DATA`, ou `COMPLETED` puis `FINISHED`.
> Pour un échange à deux parties avec une autre application réelle, utilisez deux de vos applications **en production** sur le rail `INTERNAL` : l'application destinataire doit être détectable et prouver le contrôle du portefeuille de destination par un message signé ou un dépôt de test.

## Prochaines étapes

> [card] [Widget de vérification de la propriété du portefeuille](https://docs.didit.me/fr/transaction-monitoring/wallet-ownership-widget.md)
>
> Prouvez qu'un client contrôle un portefeuille par signature, test de Satoshi, capture d'écran ou auto-déclaration.

> [card] [Interopérabilité](https://docs.didit.me/fr/transaction-monitoring/travel-rule-interoperability.md)
>
> Les endpoints destinés aux contreparties que les autres VASP utilisent pour vous joindre : TRP, rappels, récupération par e-mail, sunrise.

> [card] [Réseaux](https://docs.didit.me/fr/transaction-monitoring/travel-rule-networks.md)
>
> Ce que chaque rail et chaque réseau permet d'atteindre aujourd'hui, et ce qui nécessite encore une adhésion.

> [card] [Tester en sandbox](https://docs.didit.me/fr/transaction-monitoring/travel-rule-sandbox.md)
>
> Testez chaque résultat possible avec le VASP de test de Didit avant la mise en production.

> [card] [Soumettre des transactions](https://docs.didit.me/fr/transaction-monitoring/transactions.md)
>
> Comment inclure les données Travel Rule dans le payload de Create Transaction.

> [card] [Bibliothèque de règles](https://docs.didit.me/fr/transaction-monitoring/rules-library.md)
>
> Le catalogue complet des règles prédéfinies, y compris le lot Travel Rule.

> [card] [Webhooks](https://docs.didit.me/fr/integration/webhooks.md)
>
> Abonnez-vous à travel\_rule.status.updated et à toutes les autres familles d'événements.

> [card] [Guide de la console](https://docs.didit.me/fr/transaction-monitoring/console.md)
>
> Consulter le statut Travel Rule dans la vue de détail de la transaction.


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