Descripción general
La Travel Rule del GAFI (Recomendación 16), que en la UE se aplica mediante el Reglamento de Transferencias de Fondos (TFR), exige que el proveedor de servicios de activos virtuales que envía (el VASP ordenante) recopile y transmita la información del ordenante y del beneficiario al VASP que recibe (el VASP beneficiario) antes de que se liquide una transferencia de activos virtuales. A diferencia de la mayoría de las jurisdicciones, el TFR de la UE se aplica sin importe mínimo: todas las transferencias de criptoactivos están dentro del alcance, no solo las que superan un umbral de reporte. El producto Travel Rule de Didit es un intercambio gestionado, no un simple campo de datos:- Envías una transferencia con
transaction_category: "travelRule". - Didit prueba los canales por orden de prioridad para llegar al VASP de la contraparte, realiza el intercambio por el primero que lo alcanza y, cuando la otra parte devuelve un nombre del beneficiario, lo compara con el que enviaste.
- Solo debes emitir la transferencia on-chain cuando el intercambio llegue a
COMPLETED. - Después, comunicas a Didit el hash de la transacción on-chain para cerrar el intercambio como
FINISHED.
GTR, TRUST, VERIFYVASP, SYGNA) requieren una membresía de red en el lado emisor y, hoy por hoy, esa membresía tiene que ser tuya.
Conecta las credenciales de tu membresía en Marketplace de la consola (categoría de redes Travel Rule), y tus transferencias viajarán con tu propia identidad de red.
Hoy Didit no tiene membresía de plataforma en ninguna de las cuatro redes y está en proceso de incorporarse como proveedor en ellas.
Hasta que esa incorporación se complete, platform_available es false en las cuatro, el modo didit de la configuración (enrutar a través de la membresía de plataforma de Didit) no está disponible para ninguna red y disabled_networks no tiene nada que desactivar.
Una membresía que conectes tú siempre tiene prioridad sobre una membresía de plataforma.
No se incluye ninguna membresía de red por defecto, tampoco la de GTR.
Activar Travel Rule no activa GTR, TRUST, VerifyVASP ni Sygna.
Consulta Obtener la configuración de Travel Rule para ver los modos networks vigentes de tu aplicación, y Redes Travel Rule para saber a quién llega cada red hoy.
Si las cuatro indican unavailable, ninguna puede enrutar un intercambio: conecta tu propia membresía en Marketplace o usa una contraparte accesible por INTERNAL, TRP o EMAIL.
Cuando guardas una membresía, su código de participante debe ser el tuyo: al guardar se devuelve un 400 cuando el código es el propio código de Didit en esa red o un código que ya tiene la membresía de otra aplicación.
Una membresía de GTR necesita su clave privada ed25519 (en base64), aquella cuya clave pública está registrada en el portal de GTR: cifra los datos personales que envías y descifra los datos que recibes.
Una membresía de VerifyVASP apunta a tu propio VerifyVASP Enclave: su URL es la de tu Enclave y su token es el que tu Enclave presenta a Didit.
Incluso con tu propia membresía, un canal de red solo enruta una transferencia cuando su beneficiary_data lleva el identificador de la contraparte en esa red (counterparty_gtr_vasp_code, counterparty_trust_membership_id, counterparty_verifyvasp_id o counterparty_sygna_vasp_code), o cuando la entrada de la contraparte en el directorio la incluye en esa red (consulta Rutas).
platform_available indica si la membresía de plataforma está configurada; no es una comprobación en vivo del estado de la red. Del mismo modo, connected significa que las credenciales de tu membresía están guardadas y han superado la validación local; el primer intercambio real las verifica frente a la red. Un modo didit u own no garantiza que la contraparte sea accesible ni que la entrega se realice correctamente.
Con tu propia membresía, Didit lee la respuesta de la contraparte a través de GTR, VERIFYVASP y SYGNA a partir de la respuesta de la red o consultando la red cada 5 minutos, lo que completa o rechaza la transferencia.
TRUST solo envía: una transferencia enviada a través de ella permanece en AWAITING_COUNTERPARTY hasta que transcurre confirmation_timeout_hours, luego pasa a EXPIRED y se aplica tu timeout_outcome.
La recepción de una transferencia que otro VASP te envía a través de una red aún no está activada, y todavía no se ha completado ningún intercambio con una red de producción: consulta Redes de Travel Rule.
Otras redes (CODE, la alianza coreana, TRISA, Notabene y Veriscope) aparecen en el Marketplace de la consola como tarjetas de solicitud de acceso.
Didit aún no tiene integración con ellas, por lo que hoy ninguna transferencia puede enrutarse a través de ellas.
Cuando ninguno de los siete canales puede resolver una contraparte, Didit ejecuta un paso de resolución más antes de darse por vencido: la atribución mediante análisis de blockchain.
La wallet de destino se busca en los datos de análisis de blockchain; cuando la wallet pertenece a un exchange o custodio conocido, Didit registra el VASP atribuido en la transferencia, lo añade al directorio y vuelve a ejecutar los canales una vez con el nuevo nombre.
Consulta Atribución mediante análisis de blockchain para conocer el comportamiento completo y los precios.
Una transferencia cuyo destino está declarado como wallet sin custodia omite los siete canales (no hay un VASP de contraparte con el que intercambiar) y pasa directamente a la prueba de propiedad de la wallet del usuario final.
/v3/ descritos en esta página.Qué se ejecuta cuando envías una transferencia
Las comprobaciones se ejecutan dentro de la única llamadaPOST /v3/transactions/: cuando recibes la respuesta, el análisis ya se ha ejecutado, la transferencia está almacenada y las reglas se han evaluado.
El intercambio en sí empieza después de la respuesta: Didit elige el canal y contacta con la contraparte fuera de tu solicitud, así que ninguna red ni contraparte puede ralentizar ni hacer fallar la creación.
Cada cambio de estado posterior llega como un webhook travel_rule.status.updated que indica el canal, y el objeto de transferencia que devuelven los endpoints de transferencias de Travel Rule incluye un campo delivery que muestra si el mensaje llegó.
No hay ningún proceso que debas consultar periódicamente: las partes asíncronas son el intercambio, la respuesta de la contraparte y cualquier acción del usuario final.
Paso a paso:
- Validación y vinculación del sujeto. Se valida el payload.
Un
transaction_idque ya se ha usado en tu aplicación se rechaza con400y, cuando dos solicitudes con el mismo id compiten, una de ellas recibe409. Elsubjectse vincula a tu perfil de usuario o de empresa mediantevendor_data, de modo que la transferencia aparece en ese perfil para su monitorización. Unvendor_datade sujeto o de contraparte incluido en la lista de bloqueo se rechaza con400y no se crea ninguna transacción. Las wallets, cuentas bancarias, IP y dispositivos incluidos en la lista de bloqueo se comprueban en el paso 6 y provocan el rechazo de la transacción en ese punto, después de que se haya ejecutado el enrutamiento. - Enriquecimiento de dispositivo y red. Las IP de las partes se enriquecen con geolocalización, y cualquier contexto de dispositivo (automático con el envío desde el SDK) se normaliza en el grafo de dispositivos, el mismo grafo que alimentan las sesiones de KYC, de modo que “relacionado por dispositivo” funciona tanto en el onboarding como en las transacciones.
- Análisis cripto. En las transferencias cripto con la monitorización activada, la wallet correspondiente (o el hash on-chain) se analiza con analítica de blockchain antes de intercambiar nada: exposición a sanciones, proximidad a mezcladores y fondos robados. Consulta Análisis cripto y de wallets.
- Transferencia de Travel Rule. El motor de intercambio comprueba el umbral y respeta una declaración de wallet sin custodia, y luego almacena la transferencia:
NOT_APPLICABLEpor debajo de tu umbral,UNCONFIRMED_OWNERSHIPpara una wallet sin custodia declarada,COMPLETEDcuando la otra wallet es tu propia wallet verificada, yAWAITING_COUNTERPARTYen los demás casos, todavía sin canal. Cada cambio de estado pone en cola un eventotravel_rule.status.updated, y Didit lo envía después deltransaction.createdde la transacción (paso 7). - Creación de la verificación de propiedad de la wallet. Si la transferencia requiere que el usuario final demuestre el control y
auto_wallet_verificationestá activado, se crea una sesión del widget de propiedad de wallet y se devuelve enaction_required. - Evaluación de reglas. Se ejecutan todas las reglas activas: el paquete EU TFR reacciona al estado de la transferencia fijado en el paso 4, tus reglas personalizadas ven el resultado del análisis del paso 3, y una regla puede retener la transacción (
IN_REVIEW), rechazarla o exigir una verificación adicional (AWAITING_USER). - Facturación y notificación. Una transacción saliente se factura en una sola línea: la línea de Travel Rule cuando inició un intercambio y Transaction Screening en caso contrario. Una transferencia entrante no conlleva ninguna de las dos tarifas, y a una aplicación de sandbox nunca se le cobra.
Se devuelve la respuesta y
transaction.createdse envía a tus destinos de webhook, seguido de los eventostravel_rule.status.updateddel paso 4. - Enrutamiento, después de la respuesta. Didit recorre los siete canales por orden de prioridad; si ninguno resuelve, la atribución mediante analítica de blockchain identifica a la contraparte y se vuelve a intentar el enrutamiento por los canales una vez. El primer canal disponible se encarga del intercambio: la transferencia permanece en
AWAITING_COUNTERPARTYen ese canal mientras la contraparte responde, se liquida de inmediato cuando se identifica una contraparteINTERNAL(COMPLETEDoCOUNTERPARTY_MISMATCHED_DATA), o pasa aCOUNTERPARTY_VASP_NOT_FOUNDcuando ningún canal llega a la contraparte. Cada cambio envíatravel_rule.status.updated, y tus reglas se vuelven a ejecutar siempre que cambian el estado o el canal. Una transferencia que el enrutamiento deja enCOUNTERPARTY_VASP_NOT_FOUNDrecibe en ese momento su sesión del widget de propiedad de wallet (cuandoauto_wallet_verificationestá activado); consúltala en elaction_requiredde la transacción con Obtener transacción.
Cómo se corresponde cada paso con tus obligaciones
transaction.created ni transaction.status.updated. Las transacciones de sandbox permanecen en APPROVED salvo que una transferencia de Travel Rule que expira aplique tu timeout_outcome, y no se cobra nada.
El propio motor de intercambio de Travel Rule se ejecuta en las aplicaciones de sandbox cuando Travel Rule está activado, frente a una única contraparte: todo intercambio saliente va al VASP de prueba de Didit en el canal TEST_VASP, que responde dentro de Didit con el escenario que elijas, de modo que la transferencia se completa, se rechaza, presenta datos no coincidentes, la contraparte no está disponible, expira o queda a la espera de que se demuestre la propiedad de la wallet.
Ningún mensaje de cumplimiento llega nunca a un VASP de contraparte real, y los webhooks travel_rule.status.updated se entregan con environment: sandbox.
Entrante: el VASP de prueba puede enviarte una transferencia (Disparar transferencia entrante del VASP de prueba), y el registro (POST /v3/travel-rule/inbound/) se resuelve frente a tu libreta de direcciones de wallets igual que en una aplicación de producción: ON_HOLD, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_MISMATCHED_DATA, o COMPLETED y después FINISHED.
Consulta Probar Travel Rule en sandbox.Ciclo de vida
Cada transferencia tiene unstatus que toma uno de 13 valores.
El intercambio empieza en AWAITING_COUNTERPARTY en el caso normal, en NOT_APPLICABLE cuando el importe está por debajo de tu threshold_amount o una solicitud de pago de Lightning no indica beneficiario (consulta Lightning), o en UNCONFIRMED_OWNERSHIP cuando el destino se declaró como wallet sin custodia, y termina en uno de los estados finales: FINISHED, CANCELLED o EXPIRED.
La transferencia ejecuta su propia máquina de estados, independiente del estado de revisión de la transacción subyacente.
Un estado del intercambio nunca cambia por sí solo el estado de la transacción, con una excepción: una expiración aplica el timeout_outcome que hayas configurado (o una política de periodo de transición o de contraparte sin respuesta cuando corresponda).
En cambio, tus reglas se vuelven a ejecutar en cada transición, de modo que las reglas de la TFR de la UE o cualquier regla basada en travel_rule_status pueden mover la transacción, por ejemplo a IN_REVIEW.
Una transferencia que necesita que el usuario final demuestre el control de la wallet añade un bloque action_required a la transacción, pero su status se mantiene tal como lo dejaron tus reglas.
Lee el estado del intercambio en travel_rule.status; lee el veredicto de permitir / retener / rechazar en el propio status de la transacción.
Cada transición (y cada estado notificado de forma pasiva) también se refleja en los valores personalizados de la transacción para que las reglas puedan basarse en él: custom_values.travel_rule_status (estado canónico), custom_values.travel_rule_rail (el canal que transportó el intercambio, vacío hasta que se selecciona uno) y custom_values.travel_rule_regime (derivado del jurisdiction de tu configuración: SG → singapore, GB → uk, y cualquier código de un miembro de la UE o del EEE, como DE o FR → eu; los códigos sin correspondencia se registran en minúsculas, p. ej., US → us. Un props.travel_rule_regime que envíes tú siempre prevalece sobre el valor derivado).
CANCELLED se puede alcanzar desde cualquier estado no terminal con action: "cancel"):
Entrega y reintentos
Didit envía cada mensaje que una transferencia debe enviar al margen de la solicitud que lo originó, y lo reintenta hasta que la otra parte lo acepta. El objeto de transferencia que devuelven Finalizar, cancelar o reenviar, Confirmar la propiedad y el registro sunrise incluye el estado de entrega más reciente endelivery, al igual que el objeto travel_rule incrustado en la transacción; es null cuando la transferencia nunca debió ningún mensaje.
408, 429 o 5xx) se reintenta al cabo de 30 segundos, 2 minutos, 10 minutos, 30 minutos y 1 hora.
Cuando un primer mensaje falla definitivamente (la red lo rechazó o el sexto intento también falló), la transferencia pasa a COUNTERPARTY_VASP_NOT_REACHABLE con last_error en su timeline, y puedes reintentarla con action: "resend".
Cualquier otro mensaje que falle definitivamente queda anotado en el timeline.
delivery nunca sustituye al status de la transferencia: consulta el intercambio en status y usa delivery para ver por qué una transferencia sigue en espera.
Valores de estado heredados
Si ya enviabastravel_rule_details.status antes de esta versión, esos valores siguen funcionando y se asignan automáticamente.
También se aceptan las grafías en camelCase de cualquier estado (por ejemplo, counterpartyVaspNotFound), tanto en el envío como en los valores de condición de las reglas, y se normalizan a la forma canónica:
Configuración
Configura tu perfil de VASP y tu política de negociación una sola vez por aplicación conGET/PUT /v3/travel-rule/settings/.
PUT es una actualización parcial: envía solo los campos que quieras cambiar.
Rigor de la coincidencia de nombres
Modos de validación de la propiedad
ownership_validation_mode decide cuánta prueba requiere la propiedad de una wallet antes de que una transferencia se base en ella:
FULL y SIMPLIFIED, una transferencia posterior solo la reutiliza cuando la respalda una prueba que el modo acepta para esa transferencia.
Cuando inbound_auto_accept no encuentra ninguna wallet de la libreta de direcciones con la que coincidir, una wallet cripto que hayas registrado en las transacciones de exactamente un usuario final identifica al titular.
Didit añade esa wallet a la libreta de direcciones del usuario final como tu autodeclaración, verificada salvo que el modo sea FULL. Nunca añade una wallet que la libreta de direcciones ya incluya con otro nombre de la misma cadena (eth y ethereum), incluida una registrada como de otro VASP.
Las comprobaciones de dirección que otros VASP hacen antes de enviar (a través de GTR, Sygna y VerifyVASP) también siguen el modo: con FULL, una wallet que el usuario final solo ha declarado no se informa como tuya.
Compartición de datos
data_sharing_fields elige qué campos envía Didit a la contraparte sobre tus propios usuarios, por separado para personas físicas y empresas:
- Las personas físicas pueden compartir
name,wallet_address,address,national_id,customer_identification,date_of_birthyplace_of_birth. Las empresas usan los cinco primeros. nameywallet_addressse envían siempre.- Un tipo de entidad para el que nunca hayas elegido comparte todos los campos. Una lista vacía también es una elección: comparte solo el nombre y la wallet.
- Un
PUTfusiona por tipo de entidad, así que enviarindividualdeja intacta la elección guardada paracompany. - Una elección que no puede cumplir las obligaciones del ordenante del régimen de tu
jurisdiction(EU TFR o la Recomendación 16 del GAFI), incluida una lista vacía, se rechaza con400. Los errores aparecen bajodata_sharing_fields, con el tipo de entidad como clave.
Coincidencia por campo
matching_rules define con qué rigor se compara cada campo de los datos de la contraparte con los que tienes:
NONE, STRICT, CONTAINS, DEFAULT o FUZZY.
Valores predeterminados: name sigue a name_matching_strictness, date_of_birth y address son NONE, y account es STRICT.
Un campo que se omite conserva su valor predeterminado, y un campo que el tipo de entidad no compara (una empresa no tiene date_of_birth) se rechaza con 400.
Umbrales por jurisdicción
threshold_amount existe porque el alcance de Travel Rule varía según el regulador.
Didit usa por defecto 0.00 (la postura de la UE), de modo que se intercambian datos en todas las transferencias salvo que lo subas de forma deliberada:
threshold_amount con la cifra de tu regulador; las transferencias por debajo se resuelven como NOT_APPLICABLE sin intercambiar ningún dato de identidad.
Los umbrales cambian: confirma la cifra vigente con tu asesor de cumplimiento en lugar de tomar esta tabla como asesoramiento legal.
Modelo de datos IVMS 101
El estándar del sector para los datos que se intercambian en virtud de la Travel Rule es IVMS 101 (interVASP Messaging Standard).travel_rule_details.originator_data y travel_rule_details.beneficiary_data son payloads de persona al estilo IVMS 101: Didit los envuelve en el sobre nativo de cada canal (el canal TRP intercambia bloques IVMS101 (ivms101.2023, indicado en el campo ivms_version de la transferencia), el canal Sygna su variante de IVMS cifrada con ECIES y los canales GTR, TRUST y VERIFYVASP sus propios sobres cifrados), de modo que una sola forma de payload llega a cada contraparte en el formato que espera su protocolo.
Didit lee estas claves de cada payload de persona cuando construye el mensaje IVMS 101: first_name y last_name (o un único name o full_name), date_of_birth, place_of_birth, residence_country (o country), vendor_data (o customer_identification) y wallet_address.
Dos reglas prácticas:
- Alfabeto latino. IVMS 101 exige valores en alfabeto latino; translitera los nombres que se escriben originalmente en otro sistema de escritura. La coincidencia de nombres de Didit compara conjuntos de tokens normalizados, así que las diferencias de orden y de mayúsculas no hacen fallar un intercambio, pero sí lo harán sistemas de escritura distintos en cada lado.
- Envía datos que hayas verificado. Cuando el sujeto detrás de
vendor_dataes un usuario con una verificación KYC de Didit aprobada, Didit transmite la identidad verificada, no los valores que escribiste: consulta Datos verificados y procedencia. Para cualquier otro sujeto, Didit transmite exactamente losoriginator_dataque envías, así que constrúyelos a partir de la identidad que verificaste en el onboarding, no de un campo de formulario sin verificar. Eso es lo que exige la formulación del GAFI de “información exacta y requerida del ordenante”. En el canalINTERNAL, la aplicación del beneficiario también recibe elsubjectque enviaste (nombre,vendor_data, fecha de nacimiento, país y dirección) como la parte contraparte de su transacción entrante.
Datos verificados y procedencia
Cuando el sujeto de una transferencia es uno de tus usuarios, una persona física con una verificación KYC aprobada, Didit construye ese lado del intercambio a partir de los datos verificados:- el ordenante de una transferencia saliente que creas con
POST /v3/transactions/; - el beneficiario de una transferencia entrante que notificas con
POST /v3/transactions/, o conPOST /v3/travel-rule/inbound/cuando elholder_vendor_datade la wallet coincidente identifica a ese usuario.
POST /v3/transactions/, ambos casos se aplican a una transacción cuyo transaction_category es travel_rule mientras la Travel Rule está activada.
Los nombres verificados, la fecha y el lugar de nacimiento, el número y el tipo de documento, la dirección y el país de residencia (obtenido de esa dirección) prevalecen sobre los valores que enviaste. Un campo que la verificación no contiene conserva tu valor, al igual que una dirección completa que enviaste cuando la verificada no está lo bastante completa para transmitirse.
Los metadata de la transferencia, en el objeto travel_rule incrustado en la transacción, registran de dónde procede cada campo y dónde difieren tus valores:
*_provenance asigna a cada campo verified o caller, y *_verified_differences enumera cada campo en el que enviaste un valor distinto del verificado, sin tener en cuenta mayúsculas, espacios ni códigos de país de dos o tres letras. Mientras los datos de una transferencia entrante están enmascarados, las diferencias se omiten. Didit calcula ambos, por lo que se descartan las copias de estas claves, o de name_match, que envíes en travel_rule_details.metadata.
Enviar IVMS 101 directamente
Además de los objetos planosoriginator_data y beneficiary_data, travel_rule_details (en POST /v3/transactions/) y POST /v3/travel-rule/inbound/ aceptan IVMS 101 auténtico:
- un bloque
originatory un bloquebeneficiary, conoriginatorPersonsobeneficiaryPersonsque contienen unnaturalPersono unlegalPerson, yaccountNumber; - los mismos bloques dentro de un objeto
ivms101(oIVMS101); - una persona IVMS 101, o un bloque completo, como
originator_dataobeneficiary_data.
naturalPerson, o un legalPerson, que convierte a la parte en una empresa. Un originator_data o beneficiary_data plano no vacío prevalece sobre un bloque para el mismo lado.
400 con la clave de su ruta, por ejemplo {"travel_rule_details": {"originator.originatorPersons": ["must be a non-empty list of originator persons."]}}. En POST /v3/travel-rule/inbound/ las mismas claves están en el nivel superior, bajo ivms101. cuando usaste el envoltorio.
Datos requeridos por régimen
Mientras la Travel Rule está activada, una transferencia saliente igual o superior a tu umbral debe incluir lo que exige su régimen; de lo contrario, responde400. Solo se comprueban los lados que enviaste, los datos verificados cuentan para el ordenante y los errores aparecen bajo travel_rule_details.originator_data o travel_rule_details.beneficiary_data, por ejemplo "EU TFR requires the originator name." o "FATF Recommendation 16 requires the beneficiary wallet address.":
data_sharing_fields, porque son todo lo que envía un canal; el beneficiario se comprueba tal como lo enviaste. Cuando la transferencia cumple el régimen solo con un campo que no compartes, el 400 indica el requisito del régimen y, a continuación, los campos que comparte tu configuración.
El número de documento oficial es national_id (o document_number), el número de identificación del cliente es customer_identification (o vendor_data) y la fecha de nacimiento es date_of_birth. Un beneficiary_data.wallet_address vacío se rellena a partir de counterparty.payment_method.account_id antes de la comprobación, y un retiro UTXO cuya salida del beneficiario Didit infirió de travel_rule_details.utxo cuenta como si la incluyera. Una transferencia Lightning a la que no se aplica la Travel Rule no se comprueba.
Direcciones de viaje
Una dirección de viaje (travel address) es una cadena compacta y codificada que apunta al endpoint de consultas entrantesTRP de un VASP. Nunca expone una URL en bruto ni ninguna dirección de wallet.
Una vez activado Travel Rule, Didit genera tu propia travel_address (que se devuelve como solo lectura en la configuración) para que puedas compartirla con otros VASP que te contacten por el canal TRP.
Cada una de las wallets de tu propia libreta de direcciones recibe además una receiving_travel_address propia: una consulta enviada a ella identifica esa wallet, de modo que Didit no tiene que buscarla por número de cuenta ni por nombre del titular.
El host TRP público de Didit todavía no responde a otros VASP, así que la recepción por TRP no estará disponible hasta que lo haga: consulta Redes de Travel Rule.
Cuando conozcas la dirección de viaje de una contraparte, guárdala en la entrada de la libreta de direcciones de wallets de destino para que Didit envíe las transferencias de ese beneficiario directamente por TRP en lugar de recurrir al correo electrónico.
Didit valida las direcciones de viaje que guardas en una entrada de la libreta de direcciones de wallets o que envías en originating_vasp: cada una debe empezar por ta y decodificarse en una URL de consulta que incluya t=i, por lo que un valor de ejemplo como ta... devuelve 400.
Una beneficiary_data.travel_address en una transacción no se valida: si no se puede decodificar, se omite el canal TRP.
La codificación es Base58Check con el prefijo ta: el host y la ruta del endpoint (que siempre incluye el par de consulta t=i), más una suma de verificación de cuatro bytes con doble SHA-256, todo codificado en base58.
Por eso una dirección real tiene este aspecto y ronda los 100 caracteres:
400 con "Invalid travel address.".
Toma una dirección real de la respuesta de tu propia configuración o pídesela directamente a la contraparte.
Pruebas sin credenciales de red
No necesitas ser miembro de GTR, TRUST, VerifyVASP ni Sygna para probar las rutas que no requieren membresía. Sí necesitas una clave de API de Didit para la aplicación que estás probando.- Salida en sandbox: envía una transacción
travelRuledesde una aplicación de sandbox. Tras las comprobaciones de umbral y de wallet sin custodia, el Didit Test VASP responde a la transacción dentro de Didit con el escenario que elijas según la wallet o el nombre del beneficiario: aceptación, rechazo, nombre no coincidente, no localizable, tiempo de espera agotado o wallet sin custodia. Ninguna red, correo electrónico ni otro cliente de Didit recibe nada. - Entrada en sandbox: haz que el VASP de prueba te envíe una transferencia con Disparar una transferencia entrante del VASP de prueba, o registra una tú mismo con Registrar una transferencia entrante de Travel Rule. Prueba una wallet desconocida, una wallet no verificada, un nombre de beneficiario que no coincide y una wallet verificada que sí coincide. Así se ejercitan decisiones reales de entrada sin contactar con ninguna red.
- Dos aplicaciones de producción controladas: usa el canal
INTERNALentre dos de tus propias aplicaciones de producción (la receptora es localizable y demuestra el control de la wallet de destino con un mensaje firmado o un depósito de prueba), o una contraparte TRP controlada, para probar un intercambio entre dos partes sin credenciales de una red comercial. Las aplicaciones de producción pueden generar cargos y notificaciones habituales; usa identidades de prueba y contrapartes que controles.
Enviar una transferencia
Incluye la transferencia enPOST /v3/transactions/ estableciendo transaction_category en travelRule (o travel_rule) y adjuntando un objeto travel_rule_details (alias: travelRule).
El bloque travel_rule_details es opcional, pero en cuanto lo adjuntas, travel_rule_details.status es obligatorio: un bloque sin él devuelve 400.
Cuando Travel Rule está habilitado para tu aplicación, Didit ignora el valor de status que envías y gestiona el intercambio por sí mismo:
- Si el importe es inferior al
threshold_amountque configuraste, la transferencia se resuelve de inmediato comoNOT_APPLICABLEy no se negocia nada. - Si el método de pago de la contraparte declara el destino como una wallet sin custodia (
payment_method.method_type: "unhosted_wallet"), no se prueba ningún canal: la transferencia pasa directamente aUNCONFIRMED_OWNERSHIPy el widget de propiedad de la wallet se genera automáticamente (consulta Wallets sin custodia declaradas). - En caso contrario, los canales se ejecutan por orden de prioridad, y el primero que puede llegar a la contraparte lleva el intercambio:
INTERNAL: exactamente un cliente de Didit distinto tiene una prueba de mensaje firmado o de depósito de prueba parabeneficiary_data.wallet_address, realizada después del último cambio de titular de la wallet. Tu propia aplicación nunca cuenta, y cuando tu usuario final eligió un miembro de la red de Didit en el widget de propiedad de la wallet, solo cuenta la prueba de ese miembro. La aplicación de ese cliente está en producción (no en sandbox), tiene Travel Rule activado yis_discoverableestablecido, y es el VASP que indicaste cuando envíascounterparty_vasp_leiocounterparty_vasp_name. Una wallet que solo está autodeclarada, probada con una captura de pantalla, sin verificar o solo registrada en una libreta de direcciones nunca recibe nada a través deINTERNAL.TRP: el activo de la transferencia tiene una correspondencia de Digital Token Identifier para la red de la transferencia (consulta Identificadores de activos de TRP), y el payload incluyebeneficiary_data.travel_address, la propia entrada de libreta de direcciones del destino almacena uno, o la contraparte se resuelve en el directorio (mediantecounterparty_vasp_leiocounterparty_vasp_name) con un endpoint de TRP.GTR,TRUST,VERIFYVASP,SYGNA: conectaste tu propia membresía para esa red en Marketplace, y la contraparte se resuelve en un id de red (campo explícito o entrada del directorio; consulta Campos de resolución de la contraparte). Didit aún no tiene membresía de plataforma en estas redes (está en proceso de onboarding como proveedor), por lo quedisabled_networksno modifica este paso por ahora.EMAIL: se proporcionóbeneficiary_data.counterparty_vasp_email, ocounterparty_vasp_namese resuelve en una entrada del directorio con un correo electrónico de cumplimiento.
- Si ningún canal puede resolver una contraparte, Didit ejecuta la cadena de descubrimiento de la contraparte. Con
vasp_attribution_enabledactivado, esto incluye la atribución mediante análisis de blockchain: la wallet de destino se resuelve en el VASP al que pertenece, el nombre atribuido se escribe enbeneficiary_data.counterparty_vasp_name(cuando está vacío) y en el directorio, y los canales se vuelven a ejecutar una vez; así, una wallet atribuida a un VASP cuya entrada del directorio incluye un endpoint de TRP, un id de red o un correo electrónico de cumplimiento se enruta en esta segunda pasada. - Si la contraparte sigue sin resolverse, la transferencia pasa a
COUNTERPARTY_VASP_NOT_FOUND. En las transferencias salientes, Didit genera automáticamente el widget de propiedad de la wallet (cuandoauto_wallet_verificationestá activado) para que la transferencia aún pueda completarse como una transferencia a una wallet sin custodia, y puedes volver a ejecutar el enrutamiento más adelante conaction: "resend"cuando tengas mejores datos de la contraparte.
AWAITING_COUNTERPARTY con protocol: null, y cada resultado llega como un webhook travel_rule.status.updated.
Identificadores de activos de TRP
TRP identifica el activo transferido mediante su Digital Token Identifier (DTI) ISO 24165.
Un DTI identifica un token en una blockchain, por lo que un token multicadena tiene un DTI por red.
Didit envía transferencias a través de TRP para estos activos:
USDC son los que el emisor del token publica en su white paper de MiCA.
Para un token multicadena, la transferencia debe indicar su red en transaction_details.crypto_params.crypto_chain (por ejemplo, CELO, BASE, OP, ARB o POL).
Sin una red, o en una red sin correspondencia, la transferencia no se envía a través de TRP y Didit prueba los canales restantes: primero las redes de interoperabilidad y después el correo electrónico.
USDT todavía no tiene correspondencia.
Un token sin correspondencia de DTI, como la stablecoin propia de una empresa, requiere que su emisor registre un DTI ante la autoridad de registro ISO 24165, uno por token y por blockchain.
Una vez que existan los DTI, contacta con el soporte de Didit indicando cada DTI, su blockchain y los decimales del token, y Didit añadirá la correspondencia.
Las consultas TRP entrantes que incluyen un DTI por cadena con correspondencia se registran con la moneda del token, el importe en los decimales del token y la red.
Descubrimiento de la contraparte
Una transferencia que solo incluye una dirección de wallet llega igualmente a su contraparte siempre que alguien pueda identificarla. Antes de declarar que una transferencia saliente no se puede enrutar, Didit consulta, en este orden, y se detiene en el primer paso que identifica un VASP con una ruta:discovery de la transferencia enumera cada paso ejecutado con su outcome (match, miss o skipped), un reason legible por máquina, el vasp y el rail que encontró, y una marca de tiempo.
Un VASP elegido por ti o por el usuario final tiene prioridad sobre cualquier paso automático: el descubrimiento nunca lo sustituye por su propia suposición.
Cuando ningún paso identifica una contraparte, la transferencia pasa a COUNTERPARTY_VASP_NOT_FOUND, o a COUNTERPARTY_VASP_NOT_REACHABLE cuando se identificó un VASP pero ninguna de sus rutas está disponible para tu aplicación.
La transferencia lleva entonces un reason (awaiting_end_user, no_directory_match, directory_match_no_route o picked_member_without_proven_wallet cuando el miembro elegido por el usuario final no ha demostrado la titularidad de la wallet) y una lista de next_actions:
url en las acciones que realiza el usuario final.
Cuando hay una acción de titularidad de wallet pendiente, el bloque action_required de la transacción (tipo wallet_ownership) lleva los mismos reason y next_actions, de modo que un cliente que ya sigue action_required muestra el selector sin trabajo adicional.
Campos de resolución de la contraparte
Todo lo que Didit sabe sobre la contraparte procede detravel_rule_details.beneficiary_data.
Estos campos determinan la resolución del canal; todos son opcionales:
Estructuras de cadena
En una cadena de cuentas (cadenas EVM, Solana, Tron), una transferencia mueve fondos de una dirección a otra, y Didit lee ambas wallets directamente de la transacción. Otras tres estructuras necesitan más, y lo que Didit resolvió para ellas figura en la transferencia comochain_details (vacío, {}, en una cadena de cuentas).
chain_details.originator y chain_details.beneficiary llevan cada uno la address, su memo y la source de la que procede, y la comparación y el enrutamiento leen primero las wallets de ahí.
Transferencias UTXO
Una transacción de Bitcoin, Litecoin, Bitcoin Cash o Dogecoin gasta entradas de cualquier número de direcciones y paga cualquier número de salidas, algunas de ellas cambio que vuelve al remitente. Envía las entradas y salidas comotravel_rule_details.utxo (importes en la unidad propia de la moneda, como cadenas de texto decimales), y Didit infiere lo que no declares:
- Ordenante:
originator_data.wallet_addresscuando lo envías; si no, la dirección de entrada que más aportó (source: largest_input). - Beneficiario: la wallet declarada; si no, la única salida que no devuelve cambio a una dirección de entrada (
only_non_change_output); si no, la única salida que paga el importe de la transferencia (amount_match).
chain_details.warnings señala lo que no cuadra: declared_originator_not_an_input, declared_beneficiary_not_an_output, beneficiary_output_ambiguous (no se pudo distinguir una única salida) y beneficiary_output_amount_differs.
utxo en cualquier otra cadena devuelve un 400, y cada lista admite como máximo 1000 entradas.
Un depósito notificado usa el mismo objeto utxo (consulta Registro sunrise tras el depósito).
Redes con memo y etiqueta
En XRP, Stellar (XLM), EOS, Cosmos (ATOM) y Hedera (HBAR), un exchange recibe a todos sus clientes en una única dirección compartida y los distingue por la etiqueta de destino o el memo. Por eso el memo forma parte de la wallet: envíalo comopayment_method.memo en el participante, o como memo en originator_data / beneficiary_data (alias destinationTag), y Didit compara la dirección y el memo de forma conjunta.
- Una entrada de la libreta de direcciones con memo solo coincide con las transferencias que indican ese memo. Una entrada sin memo coincide con toda la dirección. Una transferencia que no indica ningún memo nunca llega a una entrada que sí lo tiene: en una dirección compartida, un depósito sin su etiqueta no pertenece a nadie.
- Una X-address de XRP lleva la etiqueta dentro: Didit la separa en la dirección clásica y la etiqueta (
source: x_address), de modo que una wallet registrada en cualquiera de los dos formatos coincide con una transferencia en el otro. - Cada memo de una dirección compartida es un método de pago independiente en la transacción.
- Qué acepta cada red: una etiqueta de destino de XRP es un número entero de 0 a 4294967295 (se acepta un número JSON); un memo de Stellar es un texto de hasta 28 bytes, un id de 64 bits o un hash de 32 bytes en hexadecimal; EOS admite hasta 256 bytes, Cosmos hasta 256 caracteres y Hedera hasta 100 bytes. Un memo en una cadena que no los admite (Bitcoin, cadenas EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin, Lightning) devuelve un
400.
Lightning
Un pago Lightning no tiene una dirección on-chain del beneficiario: quien paga dispone de una solicitud de pago. En una transferencia cripto de bitcoin o Lightning,payment_method.account_id puede ser esa solicitud, y Didit identifica al beneficiario a partir de ella sin contactar con ninguna red:
chain_details.beneficiary (source: invoice_payee), en la cadena LIGHTNING, y el contenido de la solicitud está en chain_details.lightning.
Cuando la solicitud no indica ningún destinatario, una transferencia saliente queda en NOT_APPLICABLE, con el motivo en chain_details.not_applicable_reason y en su timeline: LIGHTNING_NO_PAYEE_IDENTIFIER, LIGHTNING_BOLT12_BLINDED_PAYEE o LIGHTNING_LNURL_NAMES_NO_PAYEE.
Un valor se interpreta como Lightning solo en bitcoin, en LIGHTNING o cuando no se indica ninguna cadena (una Lightning Address, solo en bitcoin o LIGHTNING): en cualquier otra cadena se trata como una dirección propia de esa cadena.
INTERNAL solo llega a otro cliente de Didit a través de una wallet cuyo control ese cliente haya demostrado con un mensaje firmado o un depósito de prueba, y esas pruebas existen actualmente para Bitcoin, cadenas EVM, Solana y Tron.
Por tanto, en redes con memo, Lightning, Litecoin, Bitcoin Cash y Dogecoin, una transferencia nunca llega a otro cliente de Didit a través de INTERNAL.
La coincidencia por memo, etiqueta y Lightning sigue aplicándose a tu propia libreta de direcciones: transferencias entrantes, depósitos notificados y consultas TRP dirigidas a tus wallets.Actualizar la wallet del ordenante
Es habitual que un retiro se declare desde la wallet principal de tu cliente y se emita desde las direcciones que tuvieran los fondos. Registra la wallet de la que realmente salieron los fondos conPATCH /v3/transactions/{transaction_id}/travel-rule/:
- Solo para transferencias salientes, y nunca para una en estado
CANCELLEDoEXPIRED(409). - Cuando se conocen las entradas UTXO de la transferencia, la wallet debe ser una de ellas (de lo contrario,
400). En una red con memo, envía tambiénmemo. - Solo modifica el registro: no se vuelve a enviar nada a la contraparte y el estado no cambia.
chain_details.originatorpasa a ser la nueva wallet consource: updated, eltimelineregistra la wallet anterior y la nueva, y la contrapartidaINTERNALde la transferencia se actualiza de la misma forma.
Atribución mediante análisis de blockchain
Aunque solo indiques una dirección de wallet, Didit puede averiguar quién está al otro lado. Si ninguna vía puede enrutar la transferencia, Didit resuelve la wallet de destino con los datos de atribución de análisis de blockchain: el mismo análisis que impulsa el análisis de transacciones cripto sabe qué wallets pertenecen a qué exchanges y custodios. La atribución solo se ejecuta en transferencias salientes que incluyen tanto una wallet de destino como unchain (consulta Campos de resolución de la contraparte); sin cadena, se omite.
Cuando la wallet se atribuye a una entidad conocida, Didit:
- Registra la atribución en la transferencia: se rellena
beneficiary_data.counterparty_vasp_name(si no lo proporcionaste) y los metadatos de la transferencia incluyen un bloquevasp_attributioncon el nombre de la entidad, el tipo de entidad, el país ysource: "blockchain_analytics". - Añade la entidad al directorio de VASP (o la hace coincidir con la entrada existente), para que la atribución se mantenga estable entre transferencias.
- Vuelve a ejecutar las vías una vez con el nombre atribuido: una entrada del directorio que ya tiene un endpoint TRP hace que la transferencia se enrute de inmediato en lugar de quedar en
COUNTERPARTY_VASP_NOT_FOUND.
custom_values.aml_screening_trigger de la transacción se establece en travel_rule_attribution para que veas que el análisis se ejecutó porque el enrutamiento de la Travel Rule lo requería.
Cuando la monitorización cripto ya analizó la misma wallet en esta transacción, se reutiliza el resultado guardado: no hay una segunda llamada de análisis ni un segundo cargo.
La atribución nunca bloquea la creación de la transacción: si la consulta de análisis falla o tu saldo no alcanza para cubrirla, la transferencia simplemente pasa a COUNTERPARTY_VASP_NOT_FOUND y no se cobra nada por el intento.
Con action: "resend", se reutiliza una atribución registrada anteriormente en lugar de volver a consultarla.
Desactiva todo el paso con vasp_attribution_enabled: false en la configuración.
Consulta Precios para saber cómo se factura la llamada de atribución.
Cuando Travel Rule está desactivada, travel_rule_details (incluido un status que proporcione quien hace la llamada, sea heredado o actual) se sigue registrando en la transacción, pero de forma pasiva: no se despacha por ningún canal.
Úsalo para completar con carácter retroactivo los datos de cumplimiento que ya registraste con tu propio proceso.
Una transferencia saliente pasiva no inicia ningún intercambio, por lo que se factura como Transaction Screening y no en la línea de Travel Rule.
Con Travel Rule activada, esta solicitud se enruta por el canal EMAIL, porque indica el correo electrónico de cumplimiento de la contraparte y ningún canal anterior puede llegar al beneficiario:
originator_dataes lo que enviaste, porque el sujeto del ejemplo no tiene una verificación KYC aprobada; para un usuario verificado, Didit lo construye a partir de los datos verificados (consulta Datos verificados y procedencia). El canalEMAILmuestra al destinatario solo los campos del ordenante que compartes endata_sharing_fields.beneficiary_data.chainesETHporque la solicitud no indicaba ninguna cadena, así que Didit la tomó de la moneda.statusesAPPROVEDporque ninguna regla actuó sobre la transacción; tus propias reglas pueden establecer otro estado.- El contacto de cumplimiento recibe el enlace de recogida, y la transferencia se completa, no supera la coincidencia de nombre o se rechaza cuando responde, o expira si no responde (consulta Recogida por correo electrónico).
protocol sigue siendo null porque el canal se elige después de la respuesta (por ejemplo, EMAIL cuando proporcionaste counterparty_vasp_email): Obtener transacción lo muestra una vez elegido, y cada webhook travel_rule.status.updated posterior lo incluye en rail.
Si la wallet beneficiaria pertenece a otro cliente de Didit que demostró tener su control, el canal INTERNAL liquida todo el intercambio justo después de la respuesta, y ese webhook ya informa COMPLETED (o COUNTERPARTY_MISMATCHED_DATA). Cuando el nombre no coincide, tu transferencia queda en COUNTERPARTY_MISMATCHED_DATA y el otro cliente no recibe nada.
Sin counterparty_vasp_email, y cuando ni los canales ni la atribución pueden llegar al beneficiario, la transferencia termina más adelante en COUNTERPARTY_VASP_NOT_FOUND con una acción wallet_ownership en la transacción.
travel_rule incrustado en la transacción contiene los campos propios de la transferencia (uuid, status, protocol, required, obligations_count, originator_data, beneficiary_data, metadata), lo que Didit resolvió y comparó (chain_details, name_match, data_match, mismatched_fields, y los campos de enmascaramiento data_masked y data_unmasked), y los campos que indican en qué punto se encuentra: direction, rail, counterparty_vasp, delivery, discovery, reason y next_actions (consulta Por qué una transferencia está bloqueada).
Cada fila de GET /v3/transactions/ incluye el mismo bloque, o null para una transacción sin transferencia.
Los endpoints dedicados de transferencias que se describen a continuación también devuelven esos campos, además de ivms_version, ownership_confirmed, deadline_at, timeline y created_at, pero no obligations_count ni metadata.COMPLETED una vez que la hayas difundido on-chain:
COMPLETED; de lo contrario, la API devuelve 409.
Si tiene éxito, tanto la transferencia saliente como su contraparte del canal INTERNAL pasan a FINISHED, y el payment_txn_id de la transacción toma el valor del hash que proporcionaste.
En el canal TRP, Didit también publica el hash en el callback de confirmación de la contraparte.
Para cancelar una transferencia que aún no ha llegado a un estado final, envía en su lugar {"action": "cancel"} al mismo endpoint.
La contraparte del canal INTERNAL también se cancela y, en el canal TRP, una vez que la contraparte ha aprobado la consulta, Didit publica la cancelación en su callback de confirmación.
Una transferencia que ya está en FINISHED, CANCELLED o EXPIRED devuelve 409.
Una solicitud sin payment_txn_id ni una action compatible devuelve 400.
En una transferencia que pasó por GTR, VERIFYVASP o SYGNA, Didit notifica a la red el hash on-chain (o la cancelación) después de la respuesta, con los mismos reintentos; el campo delivery de la respuesta muestra ese mensaje.
Para reintentar una transferencia saliente que no pudo llegar a su contraparte (una que esté actualmente en COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE o NOT_ENOUGH_COUNTERPARTY_DATA), primero mejora los datos de la contraparte, por ejemplo, añadiendo una travel_address a la entrada de la wallet de destino o un contacto a la entrada del directorio de la contraparte, o catalogando el VASP en el directorio.
Después, envía {"action": "resend"} al mismo endpoint.
Didit reinicia el plazo de confirmación y vuelve a poner en cola la secuencia de enrutamiento desde el principio, con los datos almacenados en la transferencia: la respuesta muestra la transferencia de nuevo en AWAITING_COUNTERPARTY (con resend queued en su timeline), y el resultado llega como un webhook travel_rule.status.updated.
Un reenvío solicitado mientras aún se ejecuta un intento anterior se envía cuando ese intento concluye.
Cualquier otro estado, o una transferencia entrante, devuelve 409.
{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"} (el id de GET /v3/travel-rule/vasps/).
La transferencia se enruta a ese VASP y se envía de nuevo.
Solo la acepta una transferencia saliente en COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE o NOT_ENOUGH_COUNTERPARTY_DATA; cualquier otro estado devuelve 409, y un id desconocido devuelve 404.
Por qué una transferencia está bloqueada
Una transferencia enCOUNTERPARTY_VASP_NOT_FOUND o COUNTERPARTY_VASP_NOT_REACHABLE indica por qué está bloqueada y qué la hace avanzar, en cada lectura de su transacción.
Lo incluyen el bloque travel_rule de GET /v3/transactions/{transaction_id}/, el de la respuesta 201 de POST /v3/transactions/ y el de cada fila de GET /v3/transactions/, así como el objeto de transferencia con el que responde el endpoint de transferencias:
reasones la causa legible por máquina,""para cualquier otro estado.next_actionsenumera lo que hace avanzar la transferencia,[]para cualquier otro estado.deliveryes el mensaje más reciente que la transferencia debe a la otra parte:kind,status(QUEUED,SENDING,RETRYING,DELIVEREDoFAILED),rail,attempts,last_error,next_attempt_atydelivered_at, onullcuando no ha debido ninguno.discoveryindica dónde buscó Didit la contraparte, con una entrada por cada paso que ejecutó:step,outcome(match,missoskipped),reason,vasp,railyat.
email_undeliverable y delivery_failed provienen de una entrega fallida y solo aparecen en COUNTERPARTY_VASP_NOT_REACHABLE.
Una entrega fallida determina reason en primer lugar, de modo que una contraparte que Didit sí encontró nunca se notifica como una que no pudo encontrar.
Los otros cuatro provienen de la traza de descubrimiento, en cualquiera de los dos estados.
reason se borra.
La entrada de la línea de tiempo de una transición causada por una entrega fallida lleva el mismo valor en reason.
Si el proveedor de correo rechaza el correo de cumplimiento de la transferencia de ejemplo anterior, el bloque travel_rule de su transacción queda así, de forma abreviada:
delivery coincide con reason, porque el propio envío fue rechazado.
Un rechazo en un recordatorio, o un rebote notificado más tarde, deja delivery.status en DELIVERED: el primer correo sí se envió.
Consulta reason, no delivery, para saber por qué una transferencia está bloqueada.
Para solucionarlo, vuelve a indicar el correo de cumplimiento de la contraparte y después reenvía:
update_compliance_email guarda la dirección como beneficiary_data.counterparty_vasp_email de esta transferencia: pertenece solo a esta transferencia y no se añade a ninguna entrada del directorio ni a ninguna lista de contactos.
El estado no cambia, la línea de tiempo añade compliance email updated y la transferencia conserva reason: email_undeliverable hasta que el reenvío envía la solicitud a la nueva dirección.
Solo la acepta una transferencia saliente en COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE o NOT_ENOUGH_COUNTERPARTY_DATA; cualquier otra transferencia responde 409, y un compliance_email ausente o con formato incorrecto responde 400.
Wallets sin custodia declaradas
Cuando tu cliente te indique que el destino es su propia wallet sin custodia (unhosted), decláralo en la transacción en lugar de dejar que el enrutamiento descubra que no hay ningún VASP detrás: establece el tipo de método de pago de la contraparte enunhosted_wallet.
- No se intenta ningún rail (no hay ningún VASP de contraparte con el que intercambiar datos) y la transferencia se crea directamente en
UNCONFIRMED_OWNERSHIP. - Cuando
auto_wallet_verificationestá activado (el valor predeterminado), Didit genera automáticamente una sesión del widget de propiedad de wallet vinculada a la transacción y la devuelve en el bloqueaction_requiredde la transacción; los SDK la lanzan automáticamente en el envío desde el cliente. - Una vez que el cliente demuestra el control de la wallet, la transferencia ejecuta la coincidencia del nombre del beneficiario y avanza a
COMPLETED(o aCOUNTERPARTY_MISMATCHED_DATAsi la coincidencia falla).
UNCONFIRMED_OWNERSHIP no es un estado que admita reenvío: completa la prueba de propiedad (o cancela) en lugar de llamar a action: "resend".
Recepción
Confirmar la propiedad de la wallet
Una transferencia queda en espera enUNCONFIRMED_OWNERSHIP en dos casos:
- Una transferencia entrante se asocia a una entrada de la libreta de direcciones cuya propiedad todavía no está verificada.
- La wallet de la contraparte se declaró sin custodia (
counterparty.payment_method.method_type: "unhosted_wallet"), en un retiro saliente o en un depósito entrante. Consulta Wallets sin custodia declaradas.
confirmed (obligatorio, booleano) y un wallet_address opcional.
El endpoint acepta una transferencia en cualquiera de estos estados y devuelve 409 con {"detail": "Transfer is not awaiting ownership confirmation."} en cualquier otro caso:
UNCONFIRMED_OWNERSHIP, en cualquier dirección.ON_HOLDen una transferencia entrante.UNCONFIRMED_OWNERSHIP,ON_HOLDoCOUNTERPARTY_MISMATCHED_DATAen una transferenciaTRPentrante, para que un responsable de cumplimiento pueda resolver manualmente una consulta de la contraparte.
INTERNAL, registro sunrise o una wallet sin custodia declarada):
confirmed: truemarca como verificada en propiedad la entrada correspondiente de la libreta de direcciones de wallets y, a continuación, ejecuta la coincidencia de nombre con elholder_namede esa entrada:COMPLETEDsi la supera,COUNTERPARTY_MISMATCHED_DATAsi no. La transferencia equivalente en el railINTERNAL, cuando existe, obtiene el mismo resultado. En una wallet sin custodia declarada,confirmed: trueregistra que tu cliente verificó quién controla la wallet de la contraparte y completa la transferencia (COMPLETED), ya sea saliente o entrante. Una transferencia entrante cuyo depósito ya se liquidó pasa entonces directamente aFINISHED.confirmed: falsemueve ambos lados de la transferencia aCOUNTERPARTY_VASP_GENERAL_DECLINE.- La entrada correspondiente se busca en tu propia libreta de direcciones solo por la dirección de la wallet, sin tener en cuenta la cadena.
Al resolver una transferencia entrante en
ON_HOLDsin entrada correspondiente, se crea la entrada a partir delbeneficiary_data.wallet_addressde la transferencia y se marca como verificada. Una wallet sin custodia declarada es la excepción: la wallet se registra en tu libreta de direcciones como verificada en propiedad, tal como se describe más abajo. En cualquier otro caso, una entrada inexistente devuelve409con{"detail": "wallet address entry no longer available"}, que es también lo que devuelve una entrada eliminada.
TRP entrante, la misma llamada responde a la consulta de la contraparte:
confirmed: truenecesita una dirección de pago que devolver, que se toma delwallet_addressde la solicitud, después de la entrada asociada y después debeneficiary_data.wallet_address. Si no hay ninguna de las tres, la solicitud devuelve400con{"wallet_address": ["wallet_address is required to approve."]}. Didit crea o verifica la entrada, envía a la contraparte el callback de resolución de la consulta, estableceownership_confirmedy mueve la transferencia aCOMPLETED.confirmed: falseenvía una resolución de rechazo y mueve la transferencia aCOUNTERPARTY_VASP_GENERAL_DECLINE.- Si no se puede contactar con el endpoint de callback de la contraparte, la solicitud devuelve
502y la transferencia conserva su estado actual. En una aprobación, la entrada de la libreta de direcciones de wallets se crea o se marca como verificada de todos modos.
COUNTERPARTY_MISMATCHED_DATA.
Si la transferencia no indica ninguna wallet de la contraparte, pasa wallet_address en el cuerpo de la solicitud.
Cuando ni la transferencia ni la solicitud aportan una dirección de wallet, la solicitud se rechaza con 400.
- Una transferencia de red que ya se respondió a través de su red (una transferencia de VerifyVASP, o una transferencia de GTR o Sygna Bridge ya decidida) devuelve
409(consulta Transferencias desde una red).
INTERNAL nueva nunca queda en espera aquí: solo llega a un cliente que ya demostró la propiedad de la wallet. Una transferencia INTERNAL más antigua que siga en espera desde antes de esa regla mantiene ocultos los datos del remitente hasta que demuestres el control de la wallet (un mensaje firmado o un depósito de prueba), y confirmarla responde 409 hasta que lo hagas. Rechazarla sigue funcionando.
Cuando la contraparte respondió que la wallet es sin custodia (hoy la Didit Test VASP da esa respuesta en sandbox), confirmar añade la wallet a tu libreta de direcciones a nombre del beneficiario y ejecuta la coincidencia de nombre: si coincide, la transferencia se completa y la wallet se marca como verificada.
Transferencias desde una red
Didit también puede recibir transferencias de Travel Rule que una contraparte envía a través deGTR, VERIFYVASP o SYGNA.
Didit activa la recepción para cada red por separado y hoy no hay ninguna activada: también requiere que cada red registre los endpoints de Didit, y Didit todavía no es miembro de estas redes.
No se pueden recibir transferencias TRUST.
Una vez activada una red, un mensaje que entrega se gestiona así:
- Enrutamiento. El mensaje llega a tu aplicación cuando indica el código de participante de tu propia membresía conectada en esa red. Un mensaje dirigido a la membresía propia de Didit solo te llega cuando eres el único cliente activo de Didit que demostró el control de la wallet del beneficiario. Todo lo demás se rechaza, de modo que ningún cliente recibe nunca la transferencia de otro cliente.
- Decisión. Didit crea la transferencia entrante en tu aplicación y la decide como una consulta TRP: coincidencia de wallet, propiedad, coincidencia de nombre e
inbound_auto_accept. - Una sola vez. Las redes reenvían un mensaje hasta que se responde. Una repetición recibe la misma respuesta que recibió la primera copia, y nada se crea ni se decide dos veces.
- Tu revisión, a través de
GTRySYGNA. Una transferencia que queda a la espera de tu decisión (UNCONFIRMED_OWNERSHIP,ON_HOLDoCOUNTERPARTY_MISMATCHED_DATA) se decide conPOST .../travel-rule/ownership/:confirmed: truela aprueba (pasawallet_addresscuando la transferencia no indica ninguna) yconfirmed: falsela rechaza. Didit responde a la red después de la respuesta, con los mismos reintentos. Una transferencia que ya se respondió devuelve409. - Sin respuesta posterior a través de
VERIFYVASP. VerifyVASP obtiene su respuesta en el momento en que pregunta, por lo que una transferencia que necesitaría tu revisión se rechaza de inmediato y no se puede decidir más tarde (409).
Datos de la contraparte enmascarados
Hasta que se confirma la propiedad de la wallet, los datos personales de la contraparte en una transferencia entrante se devuelven enmascarados en lugar de omitirse. Se conserva la primera letra de cada palabra, y la wallet, la cadena y el enrutamiento del VASP siguen siendo legibles.data_masked es true, y la contraparte en el detalle de la transacción, en el listado y en la exportación CSV se enmascara de la misma forma.
Una vez confirmada la propiedad, aparecen los valores reales y data_unmasked registra la entrada de auditoría:
reason es ownership_confirmed, wallet_proof, address_book_match, own_verified_wallet, inquiry_approved (aprobaste una consulta TRP o un mensaje GTR o Sygna), known_payment_method (respondió la wallet del usuario final que consta en tus propios registros) o travel_address (la travel address de la wallet identificó la wallet).
Una transferencia entrante INTERNAL incluye data_unmasked con reason: wallet_proof desde el momento en que se crea, porque solo un destinatario que haya demostrado el control de la wallet la recibe.
actor es el correo electrónico del usuario de la consola o la etiqueta de la clave de API, y queda vacío cuando la decisión es automática.
El informe Registro de transferencias de la Travel Rule (CSV y PDF) enmascara los nombres de una transferencia entrante de la misma forma hasta que se confirma la propiedad. Los informes que presentas ante un regulador, como el XML de goAML y el PDF del informe regulatorio, incluyen los datos reales de la contraparte.
Información sobre discrepancias
Una transferenciaCOUNTERPARTY_MISMATCHED_DATA ahora indica los campos que difieren.
data_match contiene la comparación y mismatched_fields enumera los campos.
Ambos incluyen solo nombres de campos y resultados, nunca valores, así que siguen siendo legibles mientras los datos están enmascarados:
TRP tras una discrepancia indica al VASP ordenante qué campos difieren, por ejemplo “Beneficiary data does not match the wallet holder: name”.
Lo que el otro VASP llega a saber está limitado a propósito: mientras el nombre sea incorrecto, el nombre es el único campo que se informa (mismatched_fields: ["name"], y todos los demás campos not_compared), y una vez que el nombre coincide, se indican todos los campos que difieren. Esto se aplica al lado del remitente en un intercambio INTERNAL y a un rechazo TRP.
Un nombre que falta en ambos lados aparece como not_compared y no cuenta como discrepancia, y un país coincide por el país al que se refiere, así que ES y ESP concuerdan.
Puntuación de coincidencia del nombre
Toda transferencia cuyo nombre se comparó incluyename_match, y la entrada de la línea de tiempo que lo decidió incluye el mismo objeto junto a su detail:
scorees la similitud entre los dos nombres completos, de 0 a 100, y0cuando alguno de ellos está vacío.matchedes el resultado segúnstrictness, por lo que ambos pueden no coincidir:FUZZYdecide según su propio índice de similitud de0.85.matched_namesson las palabras del nombre de la transferencia que se encuentran en el otro nombre;mismatched_namesson las palabras que tiene uno de los lados y le faltan al otro.methodesnone,exact_token,token_subset,token_containmentosequence_ratio, paraNONE,STRICT,DEFAULT,CONTAINSyFUZZY.compared_witheswallet_holder(el titular registrado para la wallet) ocounterparty_vasp(el nombre que respondió la contraparte en la página de respuesta por correo electrónico, o la respuesta del Didit Test VASP).- El resultado de la transferencia depende de
data_match, que compara todos los campos, así que una transferencia puede serCOUNTERPARTY_MISMATCHED_DATAmientrasname_match.matchedestrue. name_matchestá en el objeto de transferencia que devuelven los endpoints de la Travel Rule y en eltravel_ruleincrustado de la transacción, y esnullhasta que se compara un nombre. El listado de transferencias y los webhooks no lo incluyen.- Los nombres se ocultan mientras las partes están enmascaradas, y en la copia del otro cliente de un intercambio
INTERNAL.
Mensajes y solicitudes de documentos
Dos VASP pueden resolver una transferencia en disputa dentro de Didit en lugar de por correo electrónico.GET y POST /v3/transactions/{transaction_id}/travel-rule/messages/ listan y envían mensajes en una transferencia:
kindesMESSAGE(valor predeterminado) oDOCUMENT_REQUEST. Una solicitud de documentos necesita al menos una entrada enrequested_documents(hasta 10); un mensaje necesita unbody(hasta 5000 caracteres) o un archivo.reply_toindica un mensaje anterior de la misma transferencia. Una respuesta del otro lado que incluya un documento cumple la solicitud de documentos a la que responde, y se establecefulfilled_aten ella. Tu propia respuesta nunca cumple tu propia solicitud.- Envía multipart con
filespara adjuntar hasta 5 archivos de 10 MB cada uno, en PDF, PNG, JPEG o WebP. Didit comprueba el contenido de cada archivo, no solo su tipo declarado, y cualquier otro archivo responde400enfiles(“Attach PDF, PNG, JPEG or WebP files only.”). - Una transferencia admite hasta 100 mensajes, de los cuales hasta 20 pueden llevar adjuntos (cada uno con hasta 5 archivos), contando ambos lados; si se supera cualquiera de los dos límites, la solicitud responde
400. Los adjuntos de los mensajes no cuentan para los documentos propios de una transacción. - Los mensajes funcionan en los canales
INTERNALyEMAIL. EnINTERNAL, el otro cliente de Didit ve el mensaje en su lado del par. EnEMAIL, se envía un correo electrónico a la contraparte, que responde en el portal de recogida. Si ese correo de notificación no se puede enviar, el mensaje se guarda de todos modos y la solicitud responde201. - La solicitud responde
409con un motivo endetailpara una transferencia porTRP,GTR,TRUST,VerifyVASPoSygna(esos protocolos no admiten mensajes), una transferencia que aún no tiene contraparte, una transferenciaINTERNALcuyos datos no coincidieron con el titular de la wallet (no llegó nada al destinatario), una transferenciaINTERNALcuyo destinatario aún no ha demostrado el control de la wallet (en ambos lados, hasta que lo haga), una transferenciaEMAILcuya contraparte ya no tiene una dirección de correo electrónico de cumplimiento, y una transferenciaEMAILcuyo enlace de recogida ha expirado o cuya transferencia está cerrada. - No se dispara ningún webhook cuando la contraparte responde. Consulta el endpoint de mensajes.
- El correo electrónico que avisa a la contraparte de un nuevo mensaje se envía en inglés.
Libreta de direcciones de wallets
Registra las wallets que controlas para que las transferencias entrantes (canalINTERNAL, consultas TRP entrantes y registros posteriores al depósito) puedan resolverse con ellas, con POST/GET /v3/travel-rule/wallet-addresses/ y PATCH/DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/.
PATCH acepta holder_name, holder_vendor_data, entity_type y travel_address, y devuelve solo esos cuatro campos.
DELETE elimina la entrada (204): su dirección queda libre para volver a registrarse y deja de coincidir de inmediato con nuevas transferencias.
memo en una red con memo y está acotada por chain.
Cuando una transferencia lleva una cadena, una entrada solo coincide si su chain tiene el mismo valor (sin distinguir mayúsculas de minúsculas) o está vacío.
Los nombres de cadena se comparan como texto, sin traducirse, así que una entrada almacenada como ethereum no coincide con una transferencia cuya cadena es ETH.
Las transferencias salientes toman su cadena de beneficiary_data.chain, después de transaction_details.crypto_params.crypto_chain, después de custom_properties.crypto_chain y después de la moneda (por ejemplo, ETH, o, para USDT y USDC, la cadena inferida de payment_reference_id), que solo se establece cuando es una cadena que Didit reconoce; los registros posteriores al depósito la toman de su campo chain, en minúsculas.
Las consultas TRP entrantes ignoran la cadena: buscan coincidencias por dirección o, cuando la consulta no nombra ninguna wallet, por el nombre del beneficiario frente a tus entradas verificadas.
POST .../travel-rule/ownership/ también busca coincidencias solo por dirección.
Cuando coinciden varias entradas, el rail INTERNAL y los registros posteriores al depósito prefieren una entrada con propiedad verificada y, después, la más antigua, y la búsqueda TRP saliente toma la entrada más antigua que almacene una travel address.
Las consultas TRP entrantes y el endpoint de propiedad toman cualquiera de las entradas coincidentes, así que mantén una sola entrada por dirección siempre que puedas.
El rail INTERNAL no coincide con ninguna entrada cuando existen entradas verificadas para la dirección en más de una aplicación distinta.Importar wallets de forma masiva
Importa hasta 10,000 wallets en una sola llamada conPOST /v3/travel-rule/wallet-addresses/import/, en JSON ({"entries": [...]} o un array sin envolver) o como archivo CSV en UTF-8 de 5 MB como máximo, con una fila de encabezado.
Cada fila identifica una wallet por su address, por address_hash (el SHA-256 de la dirección en minúsculas, de 64 caracteres hexadecimales o 44 en base64) o por ambos, junto con cualquiera de los campos de entrada anteriores.
Envía solo el hash para registrar una wallet sin revelar su dirección: coincide con las transferencias a esa dirección como cualquier otra entrada y su propiedad puede demostrarse con el widget de propiedad de wallets.
created, existing (la misma wallet, cadena y memo ya está en la libreta y no se modificó), duplicate (repite una fila anterior, indicada en duplicate_of_row) o failed con sus errors.
Una fila errónea nunca hace fallar a las demás: la respuesta es 200 cuando no ha fallado ninguna fila y 207 cuando ha fallado alguna, con los recuentos en total, created, existing, duplicate y failed.
Una celda vacía del CSV cuenta como no enviada y las columnas que la importación no lee se ignoran, así que una exportación de otra herramienta se importa tal cual.
En GET /v3/travel-rule/transfers/, cada fila incluye originator_memo y beneficiary_memo junto a las dos wallets.
Recogida por correo electrónico para contrapartes inalcanzables
Cuando ningún rail de protocolo puede llegar a la contraparte, Didit recurre aEMAIL: envía un enlace de recogida de un solo uso a beneficiary_data.counterparty_vasp_email (o a un correo electrónico obtenido a partir de beneficiary_data.counterparty_vasp_name en el registro del directorio de VASP).
El destinatario no necesita una cuenta de Didit ni una clave de API: el token del enlace es la autenticación.
Cómo se envía la solicitud:
- A las direcciones correctas. Un
counterparty_vasp_emailque pases es el único destinatario. Si no, Didit escribe a hasta tres direcciones: los contactos de la contraparte que no hayan rebotado (primero los tuyos, después los contactos de la plataforma, con prioridad para los que respondieron antes) y, si queda un hueco, el correo electrónico de cumplimiento de la entrada del directorio. Cada dirección recibe su propio correo. - En tu nombre. El correo sale de la dirección de Didit como “tu razón social vía Didit”, en el idioma de la jurisdicción de la contraparte (en inglés si no se conoce ninguna), y el enlace abre la página de respuesta en ese idioma. No recibes copia: sigue la cronología de la transferencia.
- Con recordatorios. Hasta que la contraparte responde, Didit le envía recordatorios 72, 24 y 6 horas antes de la fecha límite, al menos 12 horas después de la solicitud o del último recordatorio, y nunca después de la fecha límite.
- Con gestión de rebotes. Un rebote o una queja por spam marca como no válido el contacto al que llegó, con su motivo, y la cronología de la transferencia lo registra. Un contacto de la plataforma pasa entonces a ser no válido para todos los clientes de Didit; el contacto propio de tu aplicación, solo para tu aplicación. Cuando el servicio de entrega de correo de Didit deja de enviar por completo a una dirección (tras un rebote permanente, una queja por spam o una baja de suscripción), todos los contactos con esa dirección se marcan como no válidos (
suppressed), y vuelven a ser válidos en cuanto se reanuda el envío a esa dirección. Cuando no queda ninguna dirección a la que escribir, la transferencia pasa aCOUNTERPARTY_VASP_NOT_REACHABLE, con el motivo del servidor de correo receptor en su cronología. Un rebote permanente o una queja por spam en elcounterparty_vasp_emailque pasaste lo provoca de inmediato, porque es el único destinatario; un rebote temporal (soft_bounce) en él, o en el propio correo electrónico de cumplimiento de la entrada del directorio, deja la solicitud abierta para los recordatorios.
reference es el uuid de la transferencia. originator_data y la ivms_request IVMS 101 creada a partir de él solo incluyen los campos del ordenante que compartes en data_sharing_fields; el nombre y la wallet se incluyen siempre. fiat_amount y fiat_currency están vacíos cuando no enviaste ningún valor convertido, contact repite el valor ?contact= del enlace y decline_reasons enumera los valores que acepta decline_reason. join_invitation.eligible es true mientras la contraparte tenga una invitación vigente para unirse a Didit; el enlace de invitación en sí solo viaja en el correo electrónico.
Este endpoint devuelve la cantidad almacenada tal cual, por lo que incluye los 18 decimales completos en lugar de la forma recortada que usa el objeto de detalle de la transacción.
decision es accept o decline.
Con accept, el beneficiary_name proporcionado se compara con el beneficiary_data.name de la transferencia según tu regla de nombres: COMPLETED si coinciden y COUNTERPARTY_MISMATCHED_DATA en caso contrario. En su lugar, la contraparte puede enviar una respuesta IVMS 101 completa como ivms_response; en ese caso, su nombre de beneficiario prevalece sobre beneficiary_name.
Con decline, la transferencia pasa a COUNTERPARTY_VASP_GENERAL_DECLINE. La contraparte puede indicar un decline_reason de decline_reasons, con un decline_detail (hasta 512 caracteres, obligatorio para other), y la cronología de tu transferencia lo registra, por ejemplo counterparty declined: The beneficiary is not our customer.
La respuesta es el {"status": "..."} resultante, más decline_reason tras un rechazo. Una vez que la contraparte ha respondido, ambos endpoints devuelven 410.
Ambos endpoints devuelven 404 para un token desconocido y 410 cuando se cierra la ventana: es decir, cuando la transferencia salió de AWAITING_COUNTERPARTY o cuando ya pasó su plazo de confirmación (confirmation_timeout_hours después de la creación o del último reenvío).
La contraparte lee y responde a través del enlace de recogida con GET y POST /v3/travel-rule/pickup/{token}/messages/.
No necesita clave de API, solo acepta adjuntos PDF, PNG, JPEG o WebP y responde 410 cuando el enlace expira o la transferencia se cierra.
Un mensaje que enviaste muestra el nombre de tu VASP como author y como uploaded_by del adjunto, nunca la persona ni la clave de API que lo envió.
Depósitos sin emparejar
Un depósito entrante puede llegar antes que los datos de Travel Rule del VASP de origen. Ese depósito permanece enAWAITING_COUNTERPARTY o NOT_ENOUGH_COUNTERPARTY_DATA hasta que llegan los datos.
Enumera los depósitos que siguen en espera con una clave de API que tenga el privilegio de transacciones:
GET /v3/travel-rule/transfers/, limitadas a los depósitos entrantes que esperan los datos del VASP de origen.
Cuando los datos hayan llegado como un mensaje recibido aparte, empareja tú mismo ambos:
{transaction_uuid} es el depósito.
transaction_id es la transacción del mensaje de Travel Rule recibido.
La llamada devuelve { "deposit": ..., "message": ... }.
Responde 404 cuando alguna de las dos transacciones ya no existe, y 409 con travel_rule_match_conflict (“This deposit can no longer be matched.”) cuando alguna de las dos partes ya ha avanzado.
Registro sunrise posterior al depósito
Cuando un depósito de criptoactivos se liquida on-chain antes de que se haya producido cualquier intercambio de Travel Rule (el VASP emisor no estaba disponible o estás cargando depósitos históricos), regístralo a posteriori conPOST /v3/travel-rule/inbound/.
originating_vasp.travel_address debe ser una travel address real y con checksum válido: un valor de marcador de posición devuelve 400 con "Invalid travel address.".
originating_vasp.lei debe estar vacío o tener 20 caracteres en formato LEI (18 letras o dígitos y, a continuación, 2 dígitos); cualquier otro valor devuelve 400 con "Invalid LEI: expected 20 alphanumeric characters (ISO 17442).".
Didit solo comprueba este formato, no los dígitos de control de ISO 17442.
travelRule entrante (o reutiliza un depósito entrante que ya enviaste con el mismo hash y la misma wallet, si todavía no tiene una transferencia Travel Rule) y concilia la transferencia con tu libreta de direcciones de wallets, según la dirección y su memo:
- Ninguna entrada coincide ->
ON_HOLD. Resuélvelo conPOST .../travel-rule/ownership/:confirmed: trueañade la wallet a tu libreta de direcciones como verificada y ejecuta la comprobación del nombre; si el nombre coincide, la transferencia pasa aCOMPLETEDy después aFINISHED. - La entrada existe, pero la propiedad no está demostrada ->
UNCONFIRMED_OWNERSHIP. - El nombre del beneficiario no coincide con el
holder_namede la entrada ->COUNTERPARTY_MISMATCHED_DATA. - La entrada está verificada y el nombre coincide ->
COMPLETEDy, de inmediato,FINISHED(el depósito ya se ha liquidado).
txn_id de la nueva transacción está formado por tr-in-, los primeros 80 caracteres del txid y un hash de 12 caracteres del txid y la wallet.
created es true (HTTP 201) cuando esta llamada registró el depósito, y false (HTTP 200) cuando el mismo txid y la misma wallet_address ya estaban registrados; en ese caso, se devuelve la transferencia existente sin cambios.
En el caso de una entrada verificada, ambas transiciones ocurren antes de que el endpoint responda, por lo que la respuesta síncrona ya muestra el estado final FINISHED; el paso intermedio COMPLETED queda igualmente registrado en el timeline de la transferencia.
Cada transición dispara un webhook travel_rule.status.updated (COMPLETED y FINISHED disparan uno cada uno).
Si Didit creó una nueva transacción, envía primero transaction.created y después estos eventos.
El registro no conlleva ninguna tarifa de Travel Rule ni de monitorización.
Cuando la monitorización cripto está activada y la chain es una que Didit puede analizar, el depósito recién creado también se analiza y se factura una vez con la tarifa de Verificación y análisis de wallets.
Widget de propiedad de wallet
Para sacar una transferencia deUNCONFIRMED_OWNERSHIP, o para verificar una wallet por adelantado antes de que llegue cualquier transferencia, genera un widget alojado de propiedad de wallet con POST /v3/travel-rule/widget-session/ y envía a tu cliente a la url devuelta. El widget demuestra el control de la wallet mediante la firma de un mensaje (incluidas las cuentas de smart contract EVM, a través de ERC-1271 y ERC-6492), una prueba Satoshi, una captura de pantalla o una autodeclaración, y después marca como verificada la entrada correspondiente de la libreta de direcciones para que el intercambio pueda continuar.
La captura de pantalla es la excepción: nunca verifica la wallet por sí sola, así que tu equipo de cumplimiento la revisa y confirma la transferencia en UNCONFIRMED_OWNERSHIP con POST .../travel-rule/ownership/.
Consulta Widget de propiedad de wallet para ver el flujo completo, los métodos de prueba y la nota importante de que el widget debe abrirse en un navegador real, nunca dentro de un webview de un SDK móvil.
Directorio
GET /v3/travel-rule/vasps/ busca en el directorio de VASP: las entradas del directorio compartido más los miembros de la red de Didit. Cada fila indica cómo puede tu aplicación llegar hoy a ese VASP.
search busca coincidencias con un fragmento de nombre o alias, un LEI exacto o un dominio (counterparty.example o una URL de ese dominio). Acota los resultados con:
risk y responsiveness solo devuelven entradas del directorio. Los resultados se ordenan por nombre y se paginan con limit (por defecto 50) y offset. Una red, un código de país, una franja de riesgo o un estado de capacidad de respuesta desconocidos responden 400 indicando el campo. GET /v3/travel-rule/vasps/{vasp_id}/ devuelve una fila por su id. Tu propio perfil de VASP nunca aparece en tus propios resultados.
dd_status, dd_score, dd_score_breakdown, dd_assessed_at, dd_score_semantic, risk_level, responsiveness y essential_checks contienen la evaluación de diligencia debida compartida de una entrada del directorio. decision, entity_screening y questionnaires son los registros propios de tu aplicación sobre el VASP, y ningún otro cliente los ve. Consulta Diligencia debida de contrapartes para saber qué significa cada uno, y condiciona tus decisiones con los campos de regla de contraparte en lugar de crear bifurcaciones en tu código con ellos. Los miembros de la red de Didit no reciben puntuación: dd_status es COMPLETED, dd_score es null, risk_level está vacío y el desglose está vacío.
Rutas
routes enumera las formas en que tu aplicación puede entregar al VASP, en el orden en que las prueba la secuencia de enrutamiento, y reachable es true cuando al menos una está available:
TRPcuando la entrada tiene un endpoint TRP que el directorio verificó.GTR,TRUST,VERIFYVASP,SYGNAcuando una red registra al VASP con un código. La ruta estáavailablesolo cuando tu aplicación puede enviar hoy por esa red (via: own_membershippara tu propia membresía conectada,via: didit_membershippara la de Didit). En caso contrario estáunavailableconreason: no_membershiporeason: network_switched_off, y nunca se muestra como una forma de llegar al VASP. Las membresías de red propias de Didit aún no están activas, por lo que hoy una ruta de red solo está disponible mediante una membresía que hayas conectado en el Marketplace.EMAILcuando existe un contacto de cumplimiento para la entrada.INTERNALpara un miembro de la red de Didit.
TRISA aparece en networks e indica quién es el VASP en TRISA, pero Didit no tiene un canal TRISA, así que nunca es una ruta.
Una aplicación de sandbox solo envía al VASP de prueba de Didit, por lo que no tiene disponible ninguna ruta del directorio: todas las rutas están unavailable con reason: sandbox_application, todas las filas tienen reachable: false y reachable=true no devuelve filas.
Marcas y sus entidades
Una marca que opera a través de varias entidades reguladas es una sola fila cuyasentities son las entidades jurídicas que reciben sus transferencias; una fila de entidad apunta a su marca en umbrella. Una transferencia que nombra la marca va a una de sus entidades, elegida entre todas ellas: la de la jurisdicción indicada en beneficiary_data.counterparty_vasp_jurisdiction; si no, la entidad que la atribución de análisis de blockchain vinculó a la wallet de destino; si no, la única entidad de la marca cuando no se indica ninguna jurisdicción. Solo entonces esa entidad debe ser alcanzable; si no lo es, la transferencia nunca se envía a otra entidad ni a la marca en su lugar. Cuando ninguna entidad encaja (dos encajan por igual, o se indica una jurisdicción en la que no está ninguna), se usa la ruta propia de la marca y, si no la tiene, la transferencia queda en COUNTERPARTY_VASP_NOT_FOUND. Qué entidad jurídica recibe los datos del ordenante nunca se adivina, ni se decide según qué entidad tenga un contacto.
Varias entradas con el mismo nombre, como empresas de un grupo que comparten un nombre comercial, se distinguen con la misma regla: la de la jurisdicción indicada o, si no, la que la atribución de análisis de blockchain vinculó. Cuando ninguna de las dos elige una, no se usa ninguna, y se considera que los datos no nombran ninguna entrada del directorio.
Contactos
contacts son las direcciones a las que envía el canal EMAIL. Los contactos scope: platform sirven a todos los clientes de Didit: Didit los selecciona (source: staff), o una dirección de uno de los dominios propios del VASP respondió a una solicitud a través de su enlace de respuesta (source: learned). Los contactos scope: application son privados de tu aplicación: los que añades tú (source: application) y una dirección fuera de los dominios propios del VASP que respondió a una de tus solicitudes (source: learned). Una transferencia envía un correo primero al contacto propio de tu aplicación, luego a un contacto de plataforma que ya haya respondido antes (verified_at definido) y después a los demás.
Gestiona los contactos propios de tu aplicación con la API de contactos de contrapartes:
GET /v3/travel-rule/counterparty-contacts/enumera todos los contactos a los que tu aplicación puede enviar, incluidos los contactos de plataforma y los rebotados, del más reciente al más antiguo, paginados conlimityoffset. Filtra con?counterparty=y elidde una entrada del directorio; un valor que no sea un UUID responde400.POSTañade un contacto privado (counterparty,email,nameopcional) y responde201. Si añades una dirección que la entrada ya tiene entre tus propios contactos, se devuelve ese contacto. Una entrada desconocida responde400("No directory entry with this id.").DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/elimina uno de tus propios contactos (204). Los contactos de plataforma no se pueden eliminar y responden404.
is_invalid: true, con invalid_reason (hard_bounce, soft_bounce, spam_complaint o suppressed), invalid_detail y invalid_at, y el canal EMAIL lo omite. created_by está vacío en los contactos de la plataforma y en los contactos que añadió una clave de API.
Cómo se incorporan las entradas al directorio
- El registro provisional MiCA de proveedores de servicios de criptoactivos de ESMA, que se lee cada semana: el nombre, el LEI y el estado miembro de origen de cada proveedor.
- El directorio público de TRISA, que se lee cada semana: quién es cada miembro, su país y su inscripción en TRISA. Una inscripción que el directorio deja de mostrar durante tres semanas se elimina.
- El registro global de LEI completa el LEI, otros nombres legales y el país de una entrada, y encuentra el LEI de una entrada a partir de su nombre legal exacto en su país.
- La atribución de análisis de blockchain añade o asocia el VASP propietario de una wallet de destino y conserva el nombre atribuido como alias.
- El equipo de Didit revisa y mantiene las entradas: identificadores de enrutamiento e inscripciones en redes que ha verificado, contactos de cumplimiento, alias, marcas y sus entidades, duplicados fusionados y entradas ocultas.
originating_vasp entrante o un Travel Address, permanecen en la transferencia de ese cliente y nunca modifican una entrada compartida. Las entradas se asocian primero por LEI y después por nombre exacto o alias, y se enriquecen en lugar de duplicarse.
La red de Didit
Tu perfil de VASP es miembro de la red de Didit, de modo que otros clientes de Didit lo encuentran en el directorio y en el selector de VASP del usuario final, solo mientras se cumplan todas estas condiciones:- tu aplicación está en producción (no en sandbox) y Travel Rule está activada;
is_discoverableestrueylegal_nameestá definido;compliance_emailestá definido y confirmado: envía la confirmación conPOST /v3/travel-rule/settings/compliance-email/confirmation/y abre el enlace que recibes por correo. Cambiar la dirección requiere una nueva confirmación;- Didit no ha marcado la aplicación como de prueba, de demostración o interna.
INTERNAL entrega a una wallet cuya titularidad has demostrado, como se describe en Enviar una transferencia: requiere que tu aplicación esté en producción, que Travel Rule esté activada y is_discoverable, pero no el resto de la lista.
GET /v3/travel-rule/settings/ devuelve didit_network: listed, compliance_email_confirmed y, en missing, cada condición que aún no se cumple (travel_rule_off, sandbox_application, not_discoverable, legal_name_missing, compliance_email_missing, compliance_email_unconfirmed, excluded_by_staff, internal_organization).
Identificadores de enrutamiento
El enrutamiento saliente consulta una entrada del directorio siempre que tu transferencia identifica a la contraparte mediantecounterparty_vasp_lei o counterparty_vasp_name (un alias también sirve). Una entrada incluye un identificador por cada red en la que se puede contactar, un endpoint TRP verificado y sus contactos de cumplimiento, y routes muestra cuáles de ellos puede usar tu aplicación. Siempre puedes omitir el directorio en una transferencia concreta pasando los campos explícitos de Campos de resolución de la contraparte.
Diligencia debida de contrapartes
Antes de que los fondos de tus clientes lleguen a otro VASP, necesitas saber quién es ese VASP y si lo aceptas. La diligencia debida sobre una contraparte tiene dos capas:- La evaluación compartida. Cada entrada del directorio incluye una puntuación de diligencia debida, su nivel de riesgo, un estado de capacidad de respuesta y un informe de comprobaciones esenciales. Didit los calcula a partir de los datos propios de la plataforma, por lo que todos los clientes ven los mismos valores.
- Tus propios registros. Tu decisión sobre el VASP, tu análisis de sanciones y medios adversos sobre él, y los cuestionarios de diligencia debida que intercambiaste con él pertenecen únicamente a tu aplicación. Ningún otro cliente los ve nunca, y nunca modifican la entrada compartida.
id que muestra el directorio.
Puntuación y nivel de riesgo
dd_score va de 0 a 100 y cuanto más alta, más segura. Cada resultado lo indica en dd_score_semantic, que siempre es higher_is_safer. risk_level es la franja en la que cae la puntuación:
dd_score_breakdown muestra cada uno como {points, max, reason, detail}: max es el peso del componente, reason un código legible por máquina que puedes traducir y detail una explicación de una línea en inglés.
history lee las mismas solicitudes que la capacidad de respuesta, sin ventana temporal: un intercambio COMPLETED o FINISHED es un éxito, y un intercambio EXPIRED, COUNTERPARTY_MISMATCHED_DATA o COUNTERPARTY_VASP_GENERAL_DECLINE es un fallo. Una cancelación es un acto propio del remitente y nunca cuenta.
Didit vuelve a calcular la evaluación cuando un intercambio con la entrada se completa, finaliza, expira, presenta discrepancias o es rechazado, cuando actualiza o revisa la entrada, y para cada entrada en la actualización semanal del directorio. dd_assessed_at indica cuándo se ejecutó por última vez; una entrada que aún no se ha evaluado no tiene dd_assessed_at, tiene un dd_score de 0 y un risk_level vacío, así que consulta risk_level en lugar de la puntuación sin procesar. Los miembros de la red de Didit no se puntúan: su dd_score es null, su risk_level está vacío y su dd_score_breakdown está vacío.
Comprobaciones esenciales
essential_checks es un informe breve sobre cuatro comprobaciones que Didit ejecuta sobre cada entrada del directorio, solo a partir de las fuentes verificadas de la plataforma y nunca a partir de datos que haya reportado un cliente:
checks informa de su status (pass, fail o unknown cuando ninguna fuente registrada puede resolverla), un reason legible por máquina y un detail de una línea en inglés. passed, failed, unknown y total las cuentan, level es essential y checked_at indica cuándo se generó el informe. El componente essentials puntúa la proporción de comprobaciones resueltas que se superan, por lo que una comprobación unknown no cuenta ni a favor ni en contra de la entrada. El informe está vacío hasta que la actualización semanal del directorio comprueba una entrada por primera vez, y siempre está vacío para un miembro de la red de Didit.
Capacidad de respuesta
responsiveness indica si el VASP responde a las solicitudes de Travel Rule:
total cuenta las solicitudes que los clientes de Didit iniciaron hacia el VASP en los últimos 90 días cuyo resultado está resuelto, y answered las que respondió:
- Se cuentan: las solicitudes enviadas por una vía de red o de protocolo, y las solicitudes enviadas por correo electrónico a una dirección compartida, que es la dirección de cumplimiento de la entrada o un contacto de la plataforma.
- Nunca se cuentan: una solicitud enviada por correo electrónico a una dirección que escribió un cliente, o al contacto privado de un cliente. Ese intercambio permanece en el historial de transferencias del propio cliente y nunca etiqueta al VASP para los demás.
- Respondidas:
COMPLETED,FINISHED,COUNTERPARTY_VASP_GENERAL_DECLINEoCOUNTERPARTY_MISMATCHED_DATA. Un rechazo o una discrepancia es una respuesta: el VASP intervino. - Sin respuesta:
EXPIRED. - Ninguna de las dos: cualquier otro estado. Una solicitud
CANCELLEDes un acto propio del remitente,COUNTERPARTY_VASP_NOT_REACHABLEpuede ser un fallo del lado del remitente y una solicitud que sigue en curso aún no está resuelta.
UNKNOWN con recuentos a cero. Actúa ante una contraparte que no responde con la política de no respuesta o con el preset travel-rule-counterparty-unresponsive.
Tu decisión sobre una contraparte
Registra el dictamen de tu equipo de cumplimiento sobre una contraparte conPUT /v3/travel-rule/vasps/{vasp_id}/decision/:
- La decisión es privada de tu aplicación: ningún otro cliente la ve y nunca modifica la entrada compartida.
- Una decisión no cambia nada por sí sola y nunca cambia cómo se enruta una transferencia. Llega a tus transacciones a través del campo de regla
counterparty_dd_status: instala los presets o escribe tu propia regla sobre él. - Una decisión sobre una marca cubre cada una de sus entidades que no tenga una decisión propia, y la entidad muestra la marca en
inherited_from. Una decisión sobre la entidad prevalece sobre la de la marca para esa entidad. GETdevuelve la decisión que se aplica, o{"decision": null}.DELETEelimina tu propia decisión y devuelve lo que se aplica a continuación: la decisión de la marca para una entidad o, en otro caso,null. Responde404cuando tu aplicación no tiene una decisión propia sobre la contraparte.reasones opcional, admite hasta 255 caracteres y cadaPUTlo reemplaza.decided_byes el usuario de la consola que tomó o modificó por última vez la decisión, y queda vacío cuando lo hizo una clave de API.
Análisis de la contraparte
El análisis de la contraparte comprueba el propio VASP de la contraparte, como empresa, frente a sanciones y medios adversos. Es opcional y se factura: actívalo concounterparty_screening_enabled en tu configuración. Mientras está desactivado, no se analiza ni se factura nada.
- Facturación. Cada nuevo análisis cuesta el precio de Company AML Screening de tu organización.
GET /v3/travel-rule/vasps/{vasp_id}/screening/devuelve eseprice,fresh_daysy si el análisis está activado (enabled), sin coste. Un análisis de menos de 30 días se considera reciente:POSTlo devuelve de nuevo con200youtcome: reusedy no factura nada, salvo que envíesrefresh: true. Un nuevo análisis responde201conoutcome: screened. Las aplicaciones de sandbox nunca se analizan ni se facturan. - Análisis automático. Con el análisis activado, Didit también analiza la contraparte a la que llega un intercambio saliente cuando este comienza, antes de que tus reglas evalúen la transacción, con la misma reutilización y facturación. Un rechazo o un fallo en ese punto nunca hace fallar la transferencia.
- Resultado.
statusesApprovedcuando ninguna posible coincidencia requiere atención, yIn ReviewoDeclinedante una posible coincidencia en sanciones o medios adversos.flaggedestruemientras un análisis reciente está enIn ReviewoDeclined. - Efecto. Mientras tu último análisis de una contraparte esté marcado y el análisis siga activado,
counterparty_risk_levelesHIGHen tus reglas, de modo que el presettravel-rule-counterparty-high-risk, una vez instalado, retiene sus transferencias. Elrisk_levelcompartido del directorio no cambia. - Privacidad. El análisis es privado de tu aplicación: ningún otro cliente lo ve y nunca modifica la entrada compartida. El directorio muestra el más reciente como
entity_screening. - Rechazos.
409cuando el análisis está desactivado, para una aplicación de sandbox o mientras ya se está ejecutando un análisis de la misma contraparte.403concode: insufficient_creditscuando tu saldo no cubre el precio.502cuando no se pudo completar el análisis. Ninguno de ellos factura nada.
Cuestionario de diligencia debida
Solicita a una contraparte sus respuestas de diligencia debida con el cuestionario estándar de Didit.POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/ lo envía por correo electrónico a una dirección de cumplimiento registrada de la contraparte, con un enlace de un solo uso a una página de respuesta:
- Adónde se envía.
sent_todebe ser una dirección de cumplimiento registrada: para una entrada del directorio, su dirección de cumplimiento o un contacto que tu aplicación ve encontacts; para un miembro de la red de Didit, su correo de cumplimiento confirmado. Omítelo para usar la dirección a la que se envía un correo de Travel Rule a la contraparte: primero tu propio contacto, luego un contacto de la plataforma y, por último, la dirección de cumplimiento de la entrada. Didit nunca envía el cuestionario a ningún otro lugar y responde400cuando la contraparte no tiene ninguna dirección de cumplimiento registrada. - Respuesta. El enlace abre
/v3/travel-rule/questionnaires/{token}/, una página para una persona de la contraparte que no requiere cuenta ni clave de API. Esa persona responde a todas las preguntas, con hasta 2,000 caracteres cada una (N/Acuando alguna no aplique), y envía una sola vez. Una contraparte también puede enviar{"answers": {"<question id>": "<answer>"}}al mismo enlace. Un enlace ya usado, reemplazado o cuyo plazo ha vencido muestra en su lugar una página de expiración. El correo y la página están traducidos; las preguntas en sí están siempre en inglés, para que su significado nunca cambie entre idiomas. - Plazos. La contraparte tiene 30 días para responder (
expires_at). Las respuestas siguen vigentes durante 365 días (valid_until), ycurrentestruehasta entonces. Un cuestionario sin responder cuyo plazo ha vencido, y las respuestas que han superado su validez, pasan aEXPIRED. - Un cuestionario abierto por contraparte. Un nuevo envío reemplaza tu cuestionario anterior sin responder a la misma contraparte: pasa a
EXPIREDy su enlace deja de funcionar. Se puede enviar un correo por minuto a una dirección; un envío más rápido responde429. - Lectura de respuestas.
GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/enumera tus cuestionarios con la contraparte, del más reciente al más antiguo, conanswersindexadas por id de pregunta una vez respondidos. El directorio los muestra comoquestionnaires. Son privados de tu aplicación.
1 del formulario incluye estas doce preguntas:
Transferencias con una contraparte
GET /v3/travel-rule/transfers/?counterparty={vasp_id} lista las transferencias de la Travel Rule de tu aplicación con una contraparte, de la más reciente a la más antigua, según el id que muestra el directorio: tus transferencias cuya contraparte es esa entrada del directorio, o tus intercambios INTERNAL con ese miembro de la red de Didit. Un valor que no sea un id válido responde 400.
Cada fila incluye reason justo después de status: el propio reason de la transferencia, de modo que la lista distingue un correo de cumplimiento rebotado de una contraparte sin ruta sin tener que abrir cada transferencia.
Políticas de periodo de transición y de falta de respuesta
Cuando una transferencia expira (transcurrióconfirmation_timeout_hours mientras estaba en AWAITING_COUNTERPARTY), timeout_outcome decide qué ocurre con su transacción. Dos opciones de configuración te permiten tratar de forma distinta dos motivos habituales de expiración:
HOLD, REJECT o PROCEED, con el mismo significado que en timeout_outcome, o una cadena vacía para seguir timeout_outcome. Ambas están vacías por defecto, así que nada cambia hasta que configures una:
sunrise_policy, cuando está configurada y la jurisdicción de la contraparte está ensunrise_jurisdictions.non_responsive_policy, cuando está configurada y la contraparte está enUNRESPONSIVE.timeout_outcomeen cualquier otro caso, incluida una transferencia a un miembro de la red de Didit o a una contraparte sin entrada en el directorio.
sunrise_jurisdictions es de solo lectura y actualmente es ["CO", "KH", "VN"]. Solo estos países se consideran en periodo de transición: cualquier otra jurisdicción, así como una vacía o desconocida, sigue timeout_outcome, por lo que a una contraparte nunca se le aplica tu política de periodo de transición por accidente. El historial de la transferencia expirada indica el resultado que se aplicó, por ejemplo sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet). Tus reglas se siguen ejecutando después de la expiración, así que una regla como tr_eu_expired_review del EU TFR puede seguir enviando la transacción a revisión con PROCEED.
Campos de regla y presets
Cuatro campos de regla contienen lo que tu aplicación sabe sobre la contraparte a la que llegó el intercambio de la Travel Rule de una transacción: su entrada del directorio o, en un intercambioINTERNAL, el cliente de Didit que lo recibió. Cada campo está vacío cuando el intercambio no llegó a ninguna contraparte conocida.
travel_rule actúan sobre ellos. Instálalos desde Transactions → Rules → Library en la Business Console o con la API de la biblioteca de reglas:
Webhooks
Suscribe un destino atravel_rule.status.updated para recibir una notificación en cada cambio de estado de cada transferencia de tu aplicación, ya sea saliente o entrante, incluidos los tramos del rail INTERNAL.
El enrutamiento se ejecuta después de que se devuelve la respuesta de creación de la transacción, así que resultados como COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE o un intercambio INTERNAL que se liquida al instante te llegan a través de este evento y no en la respuesta de creación.
rail está vacío hasta que se selecciona un rail.
Para una misma transacción, Didit envía travel_rule.status.updated después de transaction.created: cuando Didit crea la transacción (tu POST /v3/transactions/, un registro sunrise, una consulta TRP entrante o el tramo entrante del rail INTERNAL), envía primero transaction.created.
Cada evento se entrega y se reintenta por separado, así que un transaction.created que necesitó un reintento puede llegar después de un evento de estado: identifica las transacciones en tu handler por transaction_id y consulta la transacción cuando un evento de estado haga referencia a una que aún no hayas visto.
Una transferencia enviada a través de TRUST no recibe ningún evento COMPLETED: TRUST no devuelve nada que Didit pueda leer, así que, salvo que el envío falle o lo canceles, su último evento es EXPIRED. A través de GTR, VERIFYVASP y SYGNA, el evento refleja la respuesta que Didit lee de la red.
Las aplicaciones de sandbox también envían este evento, con "environment": "sandbox"; una transferencia a la que respondió el Didit Test VASP incluye rail: "TEST_VASP", test_vasp: true y test_vasp_scenario.
Consulta la guía de webhooks para ver el catálogo completo de eventos, la verificación de firma y la política de reintentos.
Precios
La tarifa de Travel Rule es de $0.02 por cada intercambio saliente que se inicia, y se cobra una sola vez al crear la transacción, sin importar cuántos cambios de estado tenga el intercambio después. Unresend nunca la vuelve a cobrar.
- Se aplica a una transacción
travelRulesaliente en una aplicación con Travel Rule activada, cuyo importe es igual o superior a tuthreshold_amount, de modo que Didit inicia un intercambio gestionado para ella. Una transferencia a una wallet sin custodia, o a la wallet verificada de tu propio usuario, también cuenta: la prueba de propiedad es su intercambio. - Cualquier otra transacción saliente que incluya datos de Travel Rule se factura como Análisis de transacciones estándar, que tu plan incluye hasta su cupo mensual de transacciones y que cuesta $0.001 por encima de él: una transferencia por debajo de tu umbral, un pago Lightning al que no se aplica la Travel Rule y una transferencia registrada de forma pasiva mientras Travel Rule está desactivada. Una transacción se factura en una de las dos líneas, nunca en ambas.
- Las transacciones
travelRuleentrantes no tienen tarifa de Travel Rule ni de monitorización: el tramo entrante que Didit crea en la aplicación del beneficiario a través del railINTERNAL, las consultasTRPentrantes y los registros posteriores al depósito. - Una aplicación que se unió a Travel Rule de forma gratuita no paga ninguna de las dos tarifas en las transacciones
travelRulesalientes hasta que activa la facturación saliente conPOST /v3/travel-rule/workspace/billing/; la recepción sigue siendo gratuita. - A las aplicaciones de sandbox nunca se les cobra.
- La tarifa de Travel Rule tiene un precio de lista de $0.02 y el Análisis de transacciones de $0.001 por encima de tu cupo. A cada transacción se le cobra el precio de la línea en la que se factura.
- Si en la misma transacción también se ejecuta el análisis AML de criptomonedas o de wallets (por ejemplo, un depósito entrante con
currency_kind: "crypto"y la monitorización activada), se factura por separado a la tarifa estándar de Verificación y análisis de wallets; no está incluido en la tarifa de Travel Rule. - El análisis de contrapartes es opcional y se factura por separado, al precio del Análisis AML de empresas por cada análisis; un análisis con menos de 30 días de antigüedad se reutiliza sin coste. Las decisiones, los cuestionarios y la evaluación de diligencia debida del directorio no tienen coste.
Precio de la atribución
La atribución mediante analítica de blockchain requiere una consulta de la wallet al proveedor de analítica, y siempre obtienes el valor completo de esa consulta:- La monitorización de criptomonedas ya analizó la wallet en esta transacción: se reutiliza el resultado almacenado. Una llamada de analítica, un cargo de Verificación y análisis de wallets (el que ya generó la monitorización de criptomonedas). La atribución no añade nada.
- La monitorización de criptomonedas no se ejecutó: Didit realiza una sola vez la llamada de analítica necesaria y te proporciona todo su valor: a la transacción se le cobra la tarifa estándar de Verificación y análisis de wallets ($0.15 con la analítica gestionada por Didit) y, a cambio, recibe el resultado completo del análisis de criptomonedas (puntuación de riesgo, categoría de riesgo de la wallet, instantánea de la red), no solo el nombre del VASP. La partida de uso se marca con el disparador
travel_rule_attributionpara que el cargo sea explicable. - Clave de analítica propia: se aplica la misma tarifa de plataforma que en la monitorización de criptomonedas con tu propia clave ($0.02 por llamada). Tu proveedor te factura directamente la consulta subyacente.
- Las consultas fallidas nunca se cobran, los reenvíos reutilizan la atribución registrada sin una nueva llamada ni un nuevo cargo, y a las aplicaciones de sandbox nunca se les cobra.
Paquete de reglas EU TFR
El paquetetravel_rule_eu_tfr incluye cuatro reglas predefinidas, diseñadas específicamente para las obligaciones del TFR de la UE.
Como cualquier otro paquete predefinido, actívalo desde la Business Console en Transactions → Rules → Library.
Por qué esto funciona mejor en una sola plataforma
El cumplimiento de la Travel Rule es un problema de identidad disfrazado de problema de pagos, y Didit ya tiene la identidad:- Datos verificados disponibles. El sujeto de cada transferencia está vinculado mediante
vendor_dataal perfil KYC que Didit verificó durante el onboarding (OCR del documento, prueba de vida, coincidencia facial), así que puedes construiroriginator_dataa partir de campos verificados en la misma plataforma. Para un usuario con una verificación KYC aprobada, Didit transmite la identidad verificada en lugar de los valores que escribiste (consulta Datos verificados y procedencia). - Verificación biométrica reforzada para transferencias de riesgo. Una regla puede retener cualquier transferencia de alto valor hasta que el usuario supere una nueva comprobación biométrica contra el rostro capturado durante el onboarding, la respuesta más sólida posible a la toma de control de cuentas en el momento del retiro. Consulta el ejemplo práctico.
- Un único grafo de dispositivos. Las transferencias enviadas mediante el SDK incluyen la misma información de dispositivo que las sesiones KYC, así que “dispositivo nuevo + importe alto + beneficiario nuevo” es una sola regla, no un join en un data warehouse.
- Análisis, reglas, casos e intercambio en un solo lugar. El análisis de wallets que identifica al VASP de la contraparte es el mismo análisis que ven tus reglas y tus analistas: un solo cargo, una sola consola, un solo registro de auditoría.
- Precios publicados. $0.02 por intercambio saliente, sin niveles por volumen y sin llamadas con el equipo de ventas.
Preguntas frecuentes
Cómo funciona el intercambio
¿Didit ejecuta por sí mismo el intercambio entre VASP, o solo registra los datos de Travel Rule después de que otra red completa el handshake?
¿Didit ejecuta por sí mismo el intercambio entre VASP, o solo registra los datos de Travel Rule después de que otra red completa el handshake?
travelRule, Didit descubre la contraparte en tiempo real, envía los datos del ordenante y del beneficiario al VASP receptor por los canales que los transportan y marca la transferencia como COMPLETED solo cuando la contraparte la confirma: es un handshake gestionado, no un registro posterior a la liquidación.
La respuesta de la contraparte se procesa en todos los canales excepto TRUST, que solo envía, por lo que una transferencia por ese canal expira en lugar de completarse. Por GTR, VERIFYVASP y SYGNA, Didit lee la respuesta de la red o consultándola cada 5 minutos, y ningún intercambio por estos canales se ha completado todavía con una red activa. Por TRP, una respuesta que la contraparte no da de inmediato necesita el host TRP público de Didit, que aún no está activo (consulta Redes de Travel Rule).
Didit también conserva el registro completo y el estado de cumplimiento de cada intercambio (un ciclo de vida de 13 estados, webhooks y un registro de auditoría).
Si prefieres gestionar Travel Rule con tu propio proceso, Didit puede registrar los datos de forma pasiva como tu único sistema de registro (consulta más abajo la pregunta sobre el registro pasivo).
Ambos modos están disponibles: el intercambio activo se ejecuta cuando estableces is_enabled: true en la configuración (su valor predeterminado es false), y hasta entonces Didit registra los datos de forma pasiva.¿El intercambio es nativo de Didit o depende de un proveedor externo de interoperabilidad (Notabene, Sygna, VerifyVASP, TRP)?
¿El intercambio es nativo de Didit o depende de un proveedor externo de interoperabilidad (Notabene, Sygna, VerifyVASP, TRP)?
trp.didit.me cuando ese host público esté activo (todavía no responde, así que hoy las contrapartes no pueden contactarte por TRP), e implementa directamente los protocolos de las redes del sector: GTR (Global Travel Rule), TRUST, VerifyVASP y Sygna Bridge (implementado de forma nativa, incluidos los mensajes IVMS 101 cifrados con ECIES y las solicitudes de permiso firmadas de Sygna). Didit no revende una única capa de interoperabilidad de terceros. Para llegar a una contraparte que está en una red de membresía concreta hay que pasar necesariamente por esa red, ya que en Travel Rule no existe un canal universal único, pero el descubrimiento, el handshake, la verificación y el mantenimiento de registros los ejecuta Didit.
Hoy cada canal de red requiere tu propia membresía, y TRUST solo envía: consulta Descripción general.¿Qué redes y protocolos de Travel Rule admite Didit?
¿Qué redes y protocolos de Travel Rule admite Didit?
INTERNAL (ambos VASP en Didit), TRP (OpenVASP Travel Rule Protocol), GTR, TRUST, VerifyVASP, Sygna Bridge y una alternativa por EMAIL para contrapartes sin herramientas.
Hoy los canales de red requieren tu propia membresía, TRUST solo envía y la recepción a través de una red aún no está activada: consulta Redes de Travel Rule.
CODE, TRISA, Notabene y Veriscope aparecen en el Marketplace de Didit como fichas de solicitud de acceso, todavía sin integración.
Consulta Cómo enruta Didit un intercambio para ver la tabla de enrutamiento completa, y Redes de Travel Rule para saber a quién llega cada una hoy.¿Qué red debo usar y necesito mis propias membresías?
¿Qué red debo usar y necesito mis propias membresías?
TRP es la base que recomendamos: no requiere una membresía compartida (compartir tu travel address permite que cualquier VASP que use TRP te contacte) y, cuando una contraparte solo se conoce por su dirección de wallet, la atribución mediante análisis de blockchain la enruta por TRP solo si la entrada del directorio atribuida ya tiene un endpoint TRP.
En los envíos salientes, Didit envía por este canal los activos indicados en Identificadores de activos TRP, y una contraparte que no responde de inmediato necesita el host TRP público de Didit, que aún no está activo.
En cuanto a las redes de membresía, elige según dónde operan tus contrapartes: GTR ofrece una amplia cobertura global, TRUST está orientada a VASP de EE. UU. y VerifyVASP (que ahora también opera Sygna Bridge) tiene una gran cobertura en Asia.
Sí, hoy necesitas tu propia membresía: Didit todavía no tiene una membresía de plataforma en estas redes y está en proceso de incorporarse como proveedor.
Conecta tu membresía en Marketplace; no quedas atado a una sola red.
Recuerda que TRUST solo envía, por lo que una transferencia por ese canal no se completa, y que ningún intercambio a través de una red se ha completado todavía contra una red real.¿Cómo encuentra Didit el VASP de la contraparte cuando solo tengo una dirección de wallet?
¿Cómo encuentra Didit el VASP de la contraparte cuando solo tengo una dirección de wallet?
INTERNAL), tu propia libreta de direcciones de wallets para una travel address guardada, después los canales de protocolo y de red y, por último, la atribución mediante análisis de blockchain.
Si la transferencia solo incluye una wallet de destino, Didit la resuelve con datos de análisis de blockchain para atribuir el exchange o servicio de custodia de la contraparte y lo cataloga en el directorio de VASP.
Después, la transferencia se enruta automáticamente solo si esa entrada del directorio ya tiene una ruta, como un endpoint TRP; de lo contrario, termina en COUNTERPARTY_VASP_NOT_FOUND con el nombre de la contraparte registrado, y puedes volver a enviarla cuando exista una ruta, por ejemplo, después de guardar la travel_address de la contraparte en la entrada de tu libreta de direcciones para esa wallet (para los activos indicados en Identificadores de activos TRP).Precios y licencias
¿Cuánto cuesta Travel Rule y está incluido en mi plan de KYC o de verificación?
¿Cuánto cuesta Travel Rule y está incluido en mi plan de KYC o de verificación?
¿Se me cobrará dos veces cuando Travel Rule y la monitorización cripto se ejecuten a la vez?
¿Se me cobrará dos veces cuando Travel Rule y la monitorización cripto se ejecuten a la vez?
travelRule se factura en una sola línea: la tarifa de Travel Rule ($0.02) cuando inicia un intercambio saliente y, en caso contrario, la tarifa estándar de Transaction Screening. La consulta de análisis de blockchain se cobra una sola vez: si la monitorización cripto ya analizó la wallet de destino de la transacción, la atribución reutiliza ese resultado guardado sin coste adicional.
Los tramos entrantes que Didit genera del lado del beneficiario nunca se cobran con la tarifa de Travel Rule.¿Puede Didit proporcionar documentación o una declaración de capacidades para mi licencia de VASP o mi expediente ante el regulador?
¿Puede Didit proporcionar documentación o una declaración de capacidades para mi licencia de VASP o mi expediente ante el regulador?
Operaciones y datos
¿Qué ocurre con las transferencias por debajo de mi umbral?
¿Qué ocurre con las transferencias por debajo de mi umbral?
NOT_APPLICABLE: no se intenta ningún canal y ningún dato de identidad sale de Didit.
La transacción se factura como Transaction Screening estándar, no con la tarifa de Travel Rule.
Didit compara el umbral con el importe convertido a la moneda predeterminada de tu aplicación.
Si Didit no puede convertir el importe, el umbral no se aplica y se realiza el intercambio.
El TFR de la UE no tiene mínimo, así que los CASP de la UE deben mantener threshold_amount en 0.00.¿Qué pasa si la contraparte nunca responde?
¿Qué pasa si la contraparte nunca responde?
AWAITING_COUNTERPARTY hasta que transcurre confirmation_timeout_hours. Después, un proceso de barrido la pasa a EXPIRED y aplica tu timeout_outcome a la transacción subyacente: HOLD para revisarla, REJECT para rechazarla o PROCEED para dejarla intacta. Además, la regla predefinida tr_eu_expired_review muestra cada expiración a tus analistas. Una contraparte que nunca llega a recibir el mensaje es un caso distinto: Didit reintenta el envío y, tras el sexto intento fallido, la transferencia pasa a COUNTERPARTY_VASP_NOT_REACHABLE (consulta Entrega y reintentos).Mi contraparte no tiene ninguna herramienta de Travel Rule. ¿Puedo cumplir igualmente?
Mi contraparte no tiene ninguna herramienta de Travel Rule. ¿Puedo cumplir igualmente?
EMAIL. Su contacto de cumplimiento recibe un enlace de acceso de un solo uso y puede confirmar o rechazar el intercambio desde una página alojada, sin cuenta de Didit, sin clave de API y sin integración. Para los depósitos que ya se liquidaron antes de que fuera posible cualquier intercambio, usa el registro sunrise posterior al depósito.¿Cómo funcionan los retiros a la wallet propia del usuario?
¿Cómo funcionan los retiros a la wallet propia del usuario?
payment_method.method_type: "unhosted_wallet"). Didit omite el enrutamiento a VASP y, en su lugar, recopila la prueba de que tu cliente controla la wallet (firma de mensaje, prueba Satoshi, captura de pantalla o autodeclaración) mediante el widget de propiedad de wallet. Es la obligación del EU TFR sobre wallets sin custodia en forma de API.¿Están protegidos los datos personales de la contraparte durante el intercambio?
¿Están protegidos los datos personales de la contraparte durante el intercambio?
GTR, TRUST, VERIFYVASP, SYGNA) cifran el payload IVMS con la clave pública del VASP receptor.Ya hago el seguimiento del cumplimiento de la Travel Rule con mi propio proceso. ¿Puedo limitarme a registrarlo?
Ya hago el seguimiento del cumplimiento de la Travel Rule con mi propio proceso. ¿Puedo limitarme a registrarlo?
travel_rule_details que envíes (incluido un status aportado por quien llama, actual o heredado) se registra de forma pasiva en la transacción: no se utiliza ningún canal ni se negocia nada. Úsalo para cargar datos históricos o ejecutar tu propio proceso manteniendo un único sistema de registro.¿Cómo lo pruebo antes de pasar a producción?
¿Cómo lo pruebo antes de pasar a producción?
transaction.created y transaction.status.updated, y todo intercambio saliente va al Didit Test VASP por el canal TEST_VASP, que responde dentro de Didit con el escenario que elijas: aceptar, rechazar, discrepancia de nombre, no accesible, tiempo de espera agotado o sin custodia. Recibes webhooks travel_rule.status.updated marcados con environment: sandbox, y ningún mensaje llega a una contraparte real.
Para lo entrante, haz que el VASP de prueba te envíe una transferencia con Trigger Test VASP Inbound Transfer o registra una con POST /v3/travel-rule/inbound/; en ambos casos se decide según tu libreta de direcciones de wallets: ON_HOLD, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_MISMATCHED_DATA o COMPLETED y después FINISHED.
Para un intercambio entre dos partes con otra aplicación real, usa dos de tus propias aplicaciones de producción en el canal INTERNAL: la receptora debe ser localizable y demostrar el control de la wallet de destino con un mensaje firmado o un depósito de prueba.