Passer au contenu principal
Le SDK Didit peut soumettre des transactions surveillées directement depuis l’appareil de l’utilisateur final, y compris les transferts Travel Rule et le filtrage crypto. Votre backend crée un token à courte durée de vie et limité à un périmètre ; le SDK s’occupe du reste. Il joint automatiquement l’analyse de l’appareil et, lorsqu’un transfert Travel Rule exige une preuve de contrôle de portefeuille, il lance immédiatement ce parcours. Une session de vérification requise n’est jamais lancée automatiquement : elle est renvoyée dans la réponse pour que votre application la lance avec votre propre intégration de vérification Didit. Voir Actions requises de l’utilisateur ci-dessous.

Vue d’ensemble

La soumission côté client repose sur trois garanties :
  • Liaison du sujet à l’abri de l’usurpation. Le token est limité à un seul vendor_data (l’identifiant de votre utilisateur) au moment de sa création. L’identité du sujet de chaque transaction soumise avec ce token est appliquée côté serveur à partir de cette liaison. Un client altéré ne peut donc pas soumettre au nom d’un autre utilisateur, et le payload n’a jamais besoin de contenir des identifiants.
  • Analyse automatique de l’appareil. Le SDK collecte une empreinte de l’appareil et l’envoie avec la soumission. Didit déduit côté serveur l’adresse IP et l’agent utilisateur. Le contexte combiné de l’appareil est rattaché au sujet de la transaction, sans bibliothèque supplémentaire, sans token d’appareil distinct et sans travail d’intégration de votre part. Il alimente les règles basées sur l’appareil et la corrélation par appareil entre les sessions et les transactions.
  • Preuve de contrôle de portefeuille lancée automatiquement. Lorsque la transaction créée exige que l’utilisateur final prouve le contrôle d’un portefeuille, la réponse contient un bloc action_required de type wallet_ownership. Le SDK ouvre alors immédiatement ce parcours, puis actualise la transaction à la fin. Aucune boucle d’interrogation ni envoi de lien n’est à mettre en place de votre côté. Une action verification_session requise est différente : elle n’est jamais lancée automatiquement et revient dans le même bloc pour que votre application la lance avec sa propre intégration de vérification Didit.
L’POST /v3/transactions/ côté serveur reste le bon choix lorsqu’aucun utilisateur final n’est présent : virements back-office, imports par lots ou événements remontés par vos propres systèmes. Les deux chemins créent les mêmes enregistrements de transaction, appliquent les mêmes règles et émettent les mêmes webhooks.

Déroulement

1

Créer un token de transaction

Votre backend appelle POST /v3/transactions/sdk-token/ avec votre clé API et le vendor_data de l’utilisateur, puis transmet le sdk_token renvoyé à votre application.
2

Soumettre depuis l'appareil

L’application appelle submitTransaction sur le SDK, qui envoie le payload à POST /v1/transactions/ avec l’en-tête X-Transaction-Token et l’enrichissement automatique de l’appareil.
3

Traiter toute action requise

Si action_required.type vaut wallet_ownership, le SDK lance automatiquement le widget, puis actualise la transaction via GET /v1/transactions/{transaction_id}/. Si la valeur est verification_session, le SDK ne lance rien : lisez action_required et démarrez votre propre parcours de vérification avec son session_token (ou url sur le web).

Création d’un token de transaction

Créez les tokens depuis votre backend avec POST /v3/transactions/sdk-token/, avec la même authentification par clé API que le reste des endpoints de transactions /v3/.
Les alias en camelCase (vendorData, ttlSeconds, maxUses) sont également acceptés, comme pour le reste de l’API /v3/.
Ne placez jamais votre clé API dans une application. La clé API reste sur votre backend et crée les tokens ; seul le sdk_token à courte durée de vie est transmis à l’appareil. Créez un nouveau token pour chaque session utilisateur plutôt que de réutiliser des tokens de longue durée, et limitez max_uses au plus juste lorsque le nombre de soumissions attendues est connu.

Soumission depuis l’appareil

Les SDK envoient une requête POST /v1/transactions/ avec le token dans l’en-tête X-Transaction-Token. Le payload transmis est en camelCase. Les méthodes submitTransaction le construisent pour vous : vous ne manipulez ce format directement que si vous appelez l’endpoint depuis votre propre client HTTP :
Chaque champ de premier niveau correspond à son équivalent dans POST /v3/transactions/ : La réponse reprend le même détail de transaction en snake_case que les endpoints /v3/, auquel s’ajoute le bloc action_required :

Déclarer un portefeuille auto-hébergé

L’exemple ci-dessus déclare la destination comme le portefeuille auto-hébergé de l’utilisateur : le paymentMethod.type de la contrepartie est unhosted_wallet. Cette déclaration modifie le fonctionnement de l’échange Travel Rule :
  • Didit ne passe pas par le routage vers un VASP de la contrepartie, car aucun VASP ne se trouve derrière un portefeuille auto-hébergé, et crée directement le transfert en UNCONFIRMED_OWNERSHIP.
  • Lorsque auto_wallet_verification est activé dans vos paramètres Travel Rule (valeur par défaut), une session du widget de vérification de propriété du portefeuille est créée automatiquement et renvoyée dans action_required, exactement comme dans la réponse ci-dessus.
  • Le SDK lance automatiquement le widget. Une fois que l’utilisateur a prouvé qu’il contrôle le portefeuille, le transfert effectue la correspondance du nom et passe à COMPLETED, ou à COUNTERPARTY_MISMATCHED_DATA si le nom ne correspond pas au titulaire de l’adresse dans le carnet d’adresses du portefeuille.
Sans cette déclaration, Didit essaie d’abord les canaux dans l’ordre et ne recourt au flux de propriété que si aucun VASP de la contrepartie n’est trouvé. Déclarez le portefeuille dès que votre produit sait que la cible du retrait est le propre portefeuille de l’utilisateur : vous évitez une tentative de routage inutile et l’utilisateur accède immédiatement au parcours de preuve.

Contrôle du sujet

Trois éléments sont toujours décidés par le token, jamais par le payload :
  • L’application sous laquelle la transaction est créée.
  • L’identité du sujet : subject.externalUserId dans le payload est ignoré et remplacé par le vendor_data du token.
  • Le contexte d’appareil du sujet (voir ci-dessous) : les champs calculés côté serveur l’emportent en cas de conflit avec ce qui figure dans subject.device.
Un 401 renvoyé par cet endpoint signifie que le token a été rejeté : il est absent, inconnu, révoqué, a dépassé son max_uses ou a expiré. Les réponses pour un token expiré contiennent toujours le mot expired dans le détail de l’erreur ; c’est ainsi que les SDK distinguent les erreurs expired_token des erreurs invalid_token.

Relire la transaction

GET /v1/transactions/{transaction_id}/ renvoie le même détail, avec la même authentification par X-Transaction-Token. Les SDK l’appellent automatiquement après la fin d’un flux d’action lancé ou à la fermeture de sa fenêtre, si bien que l’état final ne dépend jamais d’un seul événement ou d’une seule notification. Vous pouvez l’appeler vous-même pour vérifier status et action_required à tout moment tant que le token est valide ; depuis votre backend, GET /v3/transactions/{transaction_id}/ renvoie le même enregistrement.

Analyse automatique de l’appareil

Chaque soumission transmet des informations sur l’appareil sans aucun travail d’intégration de votre côté :
  • Le SDK collecte une empreinte d’appareil respectueuse de la vie privée (le schéma didit-fp-v2 : signaux de plateforme et de navigateur, identifiant d’appareil persistant, score de bot et hachage composite) et l’envoie avec la requête.
  • Didit déduit côté serveur l’adresse IP, l’agent utilisateur et l’accept-language à partir de la connexion elle-même : le payload ne peut donc pas les falsifier.
Le résultat fusionné est normalisé dans le contexte d’appareil du participant sujet, puis inséré ou mis à jour dans le graphe des appareils, le même que celui alimenté par les sessions KYC. Les transactions deviennent ainsi corrélables par appareil à l’échelle de l’ensemble de vos utilisateurs (« liées par appareil »), les signaux d’appareil et de réseau alimentent les règles et l’enrichissement IP, et ils apparaissent dans le détail de la transaction dans la Console. Les transactions /v3/ soumises depuis votre serveur peuvent continuer à transmettre des objets device_context explicites pour chaque participant ; avec le chemin SDK, vous obtenez gratuitement une version plus complète de ces mêmes données.

Actions requises de l’utilisateur

Certaines transactions exigent que l’utilisateur final effectue une action avant de pouvoir aboutir : une règle peut exiger une session de vérification, ou un transfert Travel Rule peut exiger la preuve que le portefeuille de destination appartient bien à l’utilisateur. Les deux cas apparaissent au même endroit : dans le bloc action_required de la réponse à la soumission (et de chaque lecture ultérieure de la transaction) :
Si les deux cas pourraient s’appliquer à la même transaction, wallet_ownership est prioritaire, car la preuve de propriété manquante bloque l’échange Travel Rule lui-même. action_required devient null une fois l’action en attente résolue. Le champ remediation existant est conservé par souci de compatibilité ascendante ; action_required est le bloc de référence.

Contrat de lancement automatique

autoLaunchAction (par défaut true) ne lance automatiquement que l’action wallet_ownership :
  • Web : ouverte dans la fenêtre modale superposée propre au SDK, la fin du parcours étant détectée par une messagerie dont l’origine est validée, complétée par une interrogation bornée de la transaction (jamais en dépendant d’un événement unique).
  • iOS / Android (ainsi que les wrappers React Native et Flutter, qui délèguent aux SDK natifs) : présentée sous forme d’expérience native de preuve de propriété du portefeuille du SDK (SwiftUI / Jetpack Compose) lorsque useNativeWalletOwnership vaut true (valeur par défaut), avec repli sur SFSafariViewController / un Chrome Custom Tab, puis, en dernier recours, sur le navigateur par défaut. Une fois le parcours fermé ou lorsque l’application revient au premier plan, le SDK relit automatiquement la transaction.
Une action verification_session n’est jamais lancée automatiquement : elle revient toujours dans action_required, pour que votre application la démarre avec sa propre intégration de vérification Didit.
Le widget de preuve de propriété du portefeuille ne doit jamais être affiché dans une webview mobile intégrée : les liens profonds vers les portefeuilles ne peuvent pas en sortir, et le parcours de preuve se bloque. Les SDK utilisent déjà la bonne surface de navigation lors du lancement automatique ; conservez cette règle si vous gérez vous-même l’url. Consultez le guide du widget de preuve de propriété du portefeuille pour plus de détails.
Une fois le parcours de preuve de propriété du portefeuille lancé automatiquement par le SDK terminé ou fermé, le SDK relit la transaction et transmet le résultat actualisé à votre callback onActionCompleted. Une action verification_session ne fait pas partie de ce cycle, puisque votre application la lance : voir la section suivante.

Lancer une session de vérification

Lorsque action_required.type vaut verification_session, lisez le bloc dans result.actionRequired et transmettez-le à votre intégration de vérification Didit existante :
Une fois le parcours de vérification terminé, appelez GET /v1/transactions/{transaction_id}/ (ou attendez le webhook de transaction) pour récupérer le statut actualisé de la transaction.

Exemple de renforcement biométrique pour un retrait d’un montant élevé

C’est là que ce parcours est le plus utile : conditionner les transactions à risque (montants élevés, nouveaux bénéficiaires, nouveaux appareils) à une nouvelle vérification biométrique de la même personne que celle vérifiée lors de l’onboarding. La prise de contrôle de compte ressemble exactement à cela : mot de passe correct, code 2FA correct (obtenu par hameçonnage ou par échange de SIM), retrait vers un portefeuille que vous n’avez jamais vu. À ce stade, toutes les défenses fondées sur des identifiants ont déjà échoué. Le visage, lui, n’a pas échoué : l’attaquant ne peut pas réussir un selfie soumis à une détection du vivant et qui doit correspondre au visage capturé lors de l’onboarding KYC du titulaire légitime du compte.
1

Créer la règle une fois, dans la console

Transactions → Rules → Create Rule : condition amount gte 5000, action Change status → AWAITING_USER, et sélectionnez votre workflow Authentification biométrique pour cette action. Ajoutez les autres conditions de risque souhaitées (contrepartie inédite (ou contrepartie utilisée pour la première fois), nouvelle empreinte d’appareil, changement de pays de l’IP) sous forme de conditions supplémentaires ou de règles supplémentaires.
2

Soumettre le retrait comme d'habitude

Votre code de soumission ne change pas. Lorsque la règle se déclenche, la réponse renvoie status: "AWAITING_USER" et un bloc action_required de type verification_session :
3

Lancer la nouvelle vérification

Transmettez le session_token (ou le url) à votre intégration de vérification Didit existante, exactement comme dans Lancer une session de vérification. La session est créée avec le même vendor_data que le sujet de la transaction : Didit dispose donc déjà du visage de référence vérifié de l’utilisateur, issu de l’onboarding (le selfie vérifié par détection du vivant, la photo du passeport stockée sur la puce ou le portrait du document). L’utilisateur prend un seul selfie en direct ; Didit effectue une détection du vivant et une correspondance faciale 1:1 avec cette référence enregistrée. Pas de nouveau scan de document, pas de nouvel onboarding : quelques secondes de friction, uniquement sur les transactions qui le méritent.
4

Conclure selon le résultat

Lorsque la session se termine, la transaction est réévaluée automatiquement : APPROVED si la nouvelle vérification est réussie, DECLINED si elle échoue, avec un webhook transaction.status.updated dans les deux cas (reason: remediation_completed dans l’historique de la transaction). Votre application récupère le résultat via des callbacks de type onActionCompleted, via GET /v1/transactions/{transaction_id}/ ou via le webhook.
Ce fonctionnement repose sur le fait que l’onboarding KYC, la revérification biométrique et la décision sur la transaction s’exécutent sur une même plateforme : le visage capturé à l’onboarding sert de référence pour l’étape de vérification renforcée, le graphe de l’appareil relie la session à la transaction, et le moteur de règles les associe. Aucun code d’intégration entre fournisseurs, aucun export d’images faciales, aucun contrat biométrique distinct. La revérification est facturée comme une fonctionnalité standard d’Authentification biométrique ($0.10), uniquement lorsqu’elle est effectivement exécutée.

Web SDK

submitTransaction est disponible dès aujourd’hui sur le singleton du Web SDK :
Le résultat est { transactionId, status, travelRuleStatus?, actionRequired? }. Dans le payload au niveau du SDK, vous renseignez category, details et customProperties ; le SDK les convertit pour vous vers les alias du protocole (type, info, props). Les erreurs sont typées via DiditTransactionError :

SDK natifs

L’API native submitTransaction est livrée avec les SDK iOS et Android 4.1.0 (ainsi que les versions React Native et Flutter qui utilisent cette version). Contrairement au Web SDK, les payloads natifs utilisent directement les noms de champs du protocole : type, info et props.
Tous les SDK exposent aussi getTransaction(transactionToken, transactionId) pour relire la transaction avec le même token. Les SDK l’appellent pour vous après un flux d’action lancé, et vous pouvez l’appeler vous-même à tout moment. Sur mobile, le mécanisme de transport de l’empreinte de l’appareil est déjà appliqué à tous les appels réseau du SDK : les soumissions de transactions transmettent donc automatiquement les mêmes informations sur l’appareil.

Sans les SDK

Tout ce que font les SDK passe par du HTTPS standard, rien ici ne les nécessite. Deux formes d’intégration équivalentes existent : Côté serveur uniquement (sans aucun token). Soumettez avec POST /v3/transactions/ depuis votre backend, avec votre clé API. La réponse (et chaque GET /v3/transactions/{transaction_id}/ ultérieur) contient le même bloc action_required. Vous pouvez également transmettre un contexte device explicite pour chaque participant, puisqu’aucun SDK ne le collecte à votre place. Côté client avec votre propre pile HTTP. Générez un token avec POST /v3/transactions/sdk-token/, puis appelez POST /v1/transactions/ et GET /v1/transactions/{transaction_id}/ depuis l’appareil avec l’en-tête X-Transaction-Token. La même liaison au sujet et la dérivation côté serveur de l’IP et du user-agent s’appliquent. Gérez ensuite vous-même action_required : Et détectez la fin du flux sans aucun callback côté client :
  • Webhooks (recommandé). transaction.status.updated se déclenche lorsque l’action en attente fait aboutir la transaction, et travel_rule.status.updated se déclenche à chaque transition des échanges. Les deux contiennent suffisamment d’informations pour mettre à jour vos dossiers sans aucune interrogation.
  • Interrogation. Relisez la transaction (/v3/ depuis votre backend, ou /v1/ avec le token tant qu’il est valide) : action_required revient à null une fois l’action résolue, et status / travel_rule.status indiquent le résultat.
Cette conception ne comporte aucune dépendance à usage unique : le widget, la session de vérification et votre backend convergent vers le même état durable de la transaction. L’état reste donc toujours récupérable, en relisant la transaction, après la fermeture d’un onglet ou la perte d’un webhook.

Webhooks

Votre backend n’a pas besoin que l’application lui remonte l’information. Lorsqu’une transaction soumise nécessite une action de l’utilisateur, le webhook de transaction (transaction.created ou transaction.status.updated) contient le même bloc action_required. Vos systèmes connaissent donc l’action en attente, même si l’application est fermée avant la fin du flux. Une action verification_session s’accompagne du statut AWAITING_USER, mais une action wallet_ownership ne modifie pas le statut de la transaction à elle seule : lisez donc action_required plutôt que le statut. Les transitions de la Travel Rule émettent en outre travel_rule.status.updated.

Étapes suivantes

Soumission de transactions

L’API Create Transaction côté serveur et la référence complète des payloads.

Travel Rule

L’échange géré entre le donneur d’ordre et le bénéficiaire, ses paramètres et son cycle de vie.

Widget de propriété de portefeuille

Le flux hébergé de preuve de contrôle sous-jacent aux actions wallet_ownership.

Webhooks

transaction.created, transaction.status.updated et vérification de signature.

Règles

Actions de règles, y compris celles qui créent des sessions de vérification.

Statuts

Ce que signifie AWAITING_USER dans le cycle de vie de la transaction.
Dernière modification le 7 novembre 2026