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_requiredde typewallet_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 actionverification_sessionrequise 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 avecPOST /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/.
Soumission depuis l’appareil
Les SDK envoient une requêtePOST /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 :
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 : lepaymentMethod.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_verificationest 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 dansaction_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_DATAsi le nom ne correspond pas au titulaire de l’adresse dans le carnet d’adresses du portefeuille.
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.externalUserIddans le payload est ignoré et remplacé par levendor_datadu 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.
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.
/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 blocaction_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
useNativeWalletOwnershipvauttrue(valeur par défaut), avec repli surSFSafariViewController/ 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.
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.
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
Lorsqueaction_required.type vaut verification_session, lisez le bloc dans result.actionRequired et transmettez-le à votre intégration de vérification Didit existante :
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.Web SDK
submitTransaction est disponible dès aujourd’hui sur le singleton du Web SDK :
{ 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.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 avecPOST /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.updatedse déclenche lorsque l’action en attente fait aboutir la transaction, ettravel_rule.status.updatedse 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_requiredrevient ànullune fois l’action résolue, etstatus/travel_rule.statusindiquent le résultat.
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.