Saltar al contenido principal
Los SDK de Didit pueden enviar transacciones monitorizadas directamente desde el dispositivo del usuario final, incluidas las transferencias sujetas a la Travel Rule y el análisis de criptoactivos. Tu backend emite un token de corta duración y alcance limitado, y el SDK se encarga del resto. Adjunta la inteligencia del dispositivo automáticamente y, cuando una transferencia sujeta a la Travel Rule requiere una prueba de control de la wallet, inicia automáticamente ese flujo en el momento. Una sesión de verificación requerida nunca se inicia automáticamente: se devuelve en la respuesta para que tu aplicación la inicie con tu propia integración de verificación de Didit. Consulta Acciones requeridas del usuario más abajo.

Visión general

El envío desde el cliente se basa en tres garantías:
  • Vinculación del sujeto a prueba de suplantación. Al emitirse, el token queda limitado a un único vendor_data (el ID de tu usuario). La identidad del sujeto de cada transacción enviada con él se garantiza en el servidor a partir de esa vinculación, de modo que un cliente manipulado no puede enviar transacciones en nombre de otro usuario, y el payload nunca necesita incluir credenciales.
  • Inteligencia del dispositivo automática. El SDK recopila una huella digital del dispositivo y la envía junto con la transacción, y Didit obtiene la dirección IP y el user agent en el servidor. El contexto combinado del dispositivo se adjunta al sujeto de la transacción sin bibliotecas adicionales, sin un token de dispositivo aparte y sin ninguna integración extra por tu parte. Permite aplicar reglas basadas en el dispositivo y correlacionar sesiones y transacciones relacionadas por dispositivo.
  • Prueba de propiedad de la wallet iniciada automáticamente. Cuando la transacción creada requiere que el usuario final demuestre el control de una wallet, la respuesta incluye un bloque action_required de tipo wallet_ownership y el SDK abre ese flujo de inmediato y, cuando termina, actualiza la transacción. No tienes que crear bucles de sondeo ni gestionar la entrega de enlaces. Una acción verification_session requerida es diferente: nunca se inicia automáticamente y se devuelve en el mismo bloque para que tu aplicación la inicie con su propia integración de verificación de Didit.
El endpoint POST /v3/transactions/ en el servidor sigue siendo la opción adecuada cuando no hay un usuario final presente: transferencias de back office, importaciones por lotes o eventos notificados por tus propios sistemas. Ambas vías crean los mismos registros de transacciones, ejecutan las mismas reglas y emiten los mismos webhooks.

Flujo

1

Genera un token de transacción

Tu backend llama a POST /v3/transactions/sdk-token/ con tu clave de API y el vendor_data del usuario, y entrega el sdk_token devuelto a tu aplicación.
2

Envía desde el dispositivo

La aplicación llama a submitTransaction en el SDK, que envía el payload a POST /v1/transactions/ con el encabezado X-Transaction-Token y el enriquecimiento automático con datos del dispositivo.
3

Gestiona cualquier acción requerida

Si action_required.type es wallet_ownership, el SDK inicia automáticamente el widget y después actualiza la transacción mediante GET /v1/transactions/{transaction_id}/. Si es verification_session, el SDK no inicia nada: lee action_required e inicia tu propio flujo de verificación con su session_token (o con url en la web).

Emitir un token de transacción

Emite los tokens desde tu backend con POST /v3/transactions/sdk-token/, que usa la misma autenticación con clave de API que el resto de los endpoints de transacciones /v3/.
También se aceptan alias en camelCase (vendorData, ttlSeconds, maxUses), como en el resto de la API /v3/.
Nunca incluyas tu clave de API en una aplicación. La clave de API permanece en tu backend y emite los tokens; solo el sdk_token de corta duración llega al dispositivo. Emite un token nuevo por cada sesión de usuario en lugar de reutilizar tokens de larga duración, y ajusta max_uses al mínimo cuando conozcas el número de envíos previstos.

Envío desde el dispositivo

Los SDK envían una solicitud a POST /v1/transactions/ con el token en el encabezado X-Transaction-Token. El payload que viaja por la red usa camelCase. Los métodos submitTransaction lo construyen por ti, así que solo tienes que manejarlo directamente si llamas al endpoint desde tu propio cliente HTTP:
Cada campo de nivel superior corresponde a su equivalente en POST /v3/transactions/: La respuesta es el mismo detalle de transacción en snake_case que devuelven los endpoints /v3/, más el bloque action_required:

Declarar una wallet sin custodia

El ejemplo anterior declara el destino como la wallet sin custodia del propio usuario: el paymentMethod.type de la contraparte es unhosted_wallet. Esa declaración cambia la forma en que se ejecuta el intercambio de Travel Rule:
  • Didit omite por completo el enrutamiento hacia el VASP de la contraparte (detrás de una wallet sin custodia no hay ningún VASP) y crea la transferencia directamente en UNCONFIRMED_OWNERSHIP.
  • Cuando auto_wallet_verification está activado en tu configuración de Travel Rule (opción predeterminada), se genera automáticamente una sesión del widget de propiedad de wallet y se devuelve en action_required, exactamente como en la respuesta anterior.
  • El SDK abre el widget automáticamente. Cuando el usuario demuestra que controla la wallet, la transferencia ejecuta la comprobación de nombre y pasa a COMPLETED, o a COUNTERPARTY_MISMATCHED_DATA si el nombre no coincide con el titular de la wallet en la libreta de direcciones.
Sin la declaración, Didit prueba en orden las distintas vías y solo recurre al flujo de prueba de propiedad si no encuentra ningún VASP de la contraparte. Declara la wallet siempre que tu producto ya sepa que el destino de la retirada es la wallet del propio usuario: así te ahorras el intento de enrutamiento fallido y el usuario entra de inmediato en el flujo de prueba.

Control del sujeto

Hay tres cosas que siempre decide el token y nunca el payload:
  • La aplicación en la que se crea la transacción.
  • La identidad del sujeto: el subject.externalUserId del payload se ignora y se sustituye por el vendor_data del token.
  • El contexto de dispositivo del sujeto (consulta más abajo). Si hay conflicto con cualquier dato enviado en subject.device, prevalecen los campos obtenidos en el servidor.
Un 401 de este endpoint significa que el token se rechazó: falta, es desconocido, se revocó, superó su max_uses o expiró. Las respuestas de token expirado siempre contienen la palabra expired en el detalle del error, y así es como los SDK distinguen los errores expired_token de los invalid_token.

Consultar la transacción

GET /v1/transactions/{transaction_id}/ devuelve el mismo detalle con la misma autenticación X-Transaction-Token. Los SDK lo llaman automáticamente cuando termina un flujo de acción abierto o se cierra su ventana, de modo que el estado final nunca depende de un único evento o notificación. Úsalo tú mismo para volver a comprobar status y action_required en cualquier momento mientras el token sea válido. Desde tu backend, GET /v3/transactions/{transaction_id}/ devuelve el mismo registro.

Inteligencia de dispositivo automática

Cada envío incluye inteligencia de dispositivo sin que tengas que hacer ningún trabajo de integración:
  • El SDK recopila una huella del dispositivo que respeta la privacidad (el esquema didit-fp-v2: señales de la plataforma y del navegador, un identificador de dispositivo persistente, una puntuación de bot y un hash compuesto) y la envía con la solicitud.
  • Didit obtiene en el servidor la dirección IP, el user agent y el accept-language a partir de la propia conexión, por lo que no se pueden falsificar desde el payload.
El resultado combinado se normaliza y se incorpora al contexto de dispositivo del participante sujeto y se inserta o actualiza en el grafo de dispositivos, el mismo que alimentan las sesiones de KYC. Esto permite correlacionar transacciones por dispositivo en toda tu base de usuarios (“relacionadas por dispositivo”), aporta señales de dispositivo y de red a las reglas y al enriquecimiento de IP, y se muestra en el detalle de la transacción en la Console. Las transacciones /v3/ enviadas desde el servidor pueden seguir incluyendo objetos device_context explícitos por participante. Con el SDK obtienes gratis una versión más sólida de los mismos datos.

Acciones requeridas del usuario

Algunas transacciones necesitan que el usuario final haga algo antes de poder liquidarse: una regla puede exigir una sesión de verificación, o una transferencia sujeta a la Travel Rule puede exigir una prueba de que la wallet de destino pertenece realmente al usuario. Ambos casos aparecen en el mismo lugar: el bloque action_required de la respuesta del envío (y de cada lectura posterior de la transacción):
Si ambas pudieran aplicarse a la misma transacción, wallet_ownership tiene prioridad, porque la falta de la prueba de propiedad bloquea el propio intercambio de la Travel Rule. action_required pasa a ser null en cuanto se resuelve la acción pendiente. El campo remediation existente se mantiene por compatibilidad con versiones anteriores; action_required es el bloque canónico.

Contrato de lanzamiento automático

autoLaunchAction (por defecto true) solo lanza automáticamente la acción wallet_ownership:
  • Web: se abre en un modal superpuesto del propio SDK, y la finalización se detecta mediante mensajería con validación de origen más una consulta periódica acotada de la transacción (nunca depende de un único evento).
  • iOS / Android (y los wrappers de React Native y Flutter, que delegan en los SDK nativos): se presenta como la experiencia nativa de propiedad de wallet del SDK (SwiftUI / Jetpack Compose) cuando useNativeWalletOwnership es true (el valor por defecto), y recurre, si no es posible, a SFSafariViewController / una Chrome Custom Tab y, por último, al navegador predeterminado. Cuando el flujo se cierra o la aplicación vuelve al primer plano, el SDK vuelve a leer la transacción automáticamente.
Una acción verification_session nunca se lanza automáticamente: siempre vuelve en action_required para que tu aplicación la inicie con su propia integración de verificación de Didit.
El widget de propiedad de wallet nunca debe mostrarse dentro de un webview móvil integrado: los deep links de las wallets no pueden salir de él y el flujo de prueba queda sin salida. Los SDK ya usan la superficie de navegador correcta al lanzarlo automáticamente; respeta esa regla si gestionas tú mismo la url. Consulta la guía del widget de propiedad de wallet para más detalles.
Cuando el flujo de propiedad de wallet lanzado automáticamente por el SDK se completa o se cierra, el SDK vuelve a leer la transacción y entrega el resultado actualizado a tu callback onActionCompleted. Una acción verification_session no forma parte de ese ciclo, ya que la lanza tu aplicación; consulta la siguiente sección.

Lanzar una sesión de verificación

Cuando action_required.type es verification_session, lee el bloque de result.actionRequired y pásalo a tu integración de verificación de Didit existente:
Cuando termine tu flujo de verificación, llama a GET /v1/transactions/{transaction_id}/ (o espera al webhook de transacciones) para obtener el estado actualizado de la transacción.

Ejemplo: biometría reforzada para un retiro de alto valor

Este es el flujo en su mejor versión: condicionar las transacciones de riesgo (importes elevados, beneficiarios nuevos, dispositivos nuevos) a una nueva comprobación biométrica de la misma persona que verificaste en el onboarding. Una toma de control de cuenta se ve exactamente así: contraseña correcta, código 2FA correcto (obtenido por phishing o mediante SIM swapping) y un retiro a una wallet que nunca has visto. Para entonces, todas las defensas basadas en credenciales ya han fallado. Un rostro no: el atacante no puede superar una selfie con prueba de vida que debe coincidir con el rostro capturado durante el onboarding KYC del titular legítimo.
1

Crea la regla una sola vez, en la consola

Transactions → Rules → Create Rule: condición amount gte 5000, acción Change status → AWAITING_USER, y selecciona tu flujo de Biometric Authentication en la acción. Añade cualquier otra condición de riesgo que quieras (contraparte nueva, nueva huella del dispositivo, cambio de país de la IP) como condiciones o reglas adicionales.
2

Envía el retiro como de costumbre

No cambia nada en tu código de envío. Cuando la regla se dispara, la respuesta incluye status: "AWAITING_USER" y un bloque action_required de tipo verification_session:
3

Lanza la nueva comprobación

Pasa el session_token (o la url) a tu integración de verificación de Didit existente, exactamente como en Lanzar una sesión de verificación. Como la sesión se crea para el mismo vendor_data que el sujeto de la transacción, Didit ya tiene el rostro de referencia verificado del usuario desde el onboarding: la selfie con prueba de vida aprobada, la foto del chip del pasaporte o el retrato del documento. El usuario se hace una sola selfie en vivo; Didit ejecuta la prueba de vida y una coincidencia facial 1:1 con esa referencia almacenada. Sin volver a escanear el documento ni repetir el onboarding: unos segundos de fricción, solo en las transacciones que lo merecen.
4

Liquida según el resultado

Cuando la sesión se completa, la transacción se reevalúa automáticamente: APPROVED si la nueva comprobación fue exitosa, DECLINED si falló, con un webhook transaction.status.updated en ambos casos (reason: remediation_completed en el historial de la transacción). Tu aplicación obtiene el resultado mediante callbacks del tipo onActionCompleted, GET /v1/transactions/{transaction_id}/ o el webhook.
Esto funciona porque el onboarding KYC, la reverificación biométrica y la decisión sobre la transacción se ejecutan en una sola plataforma: el rostro capturado en el onboarding es la referencia para la verificación adicional, el grafo de dispositivos vincula la sesión con la transacción y el motor de reglas lo une todo, sin integraciones entre proveedores, sin exportar imágenes faciales y sin un contrato de biometría aparte. La reverificación se factura como una función estándar de Autenticación biométrica ($0.10) solo cuando realmente se ejecuta.

SDK web

submitTransaction ya está disponible en el singleton del SDK web:
El resultado es { transactionId, status, travelRuleStatus?, actionRequired? }. En el payload del SDK escribes category, details y customProperties; el SDK los asigna por ti a los alias de transmisión (type, info, props). Los errores están tipados mediante DiditTransactionError:

SDK nativos

La API nativa submitTransaction se incluye en los SDK de iOS y Android 4.1.0 (y en las versiones de React Native y Flutter que fijan esta versión). A diferencia del SDK web, los payloads nativos usan directamente los nombres de campo que se envían por la red: type, info y props.
Todos los SDK exponen también getTransaction(transactionToken, transactionId) para volver a leer la transacción con el mismo token: los SDK la invocan por ti después de un flujo de acción iniciado, y tú puedes llamarla en cualquier momento. En dispositivos móviles, el transporte de la huella del dispositivo ya se aplica a todas las llamadas de red del SDK, por lo que los envíos de transacciones llevan automáticamente la misma inteligencia del dispositivo.

Sin los SDK

Todo lo que hacen los SDK se realiza mediante HTTPS estándar: no necesitas los SDK para nada de esto. Hay dos formas de integración equivalentes: Solo del lado del servidor (sin tokens). Envía la solicitud con POST /v3/transactions/ desde tu backend usando tu clave de API. La respuesta (y cada GET /v3/transactions/{transaction_id}/ posterior) incluye el mismo bloque action_required. También puedes pasar un contexto device explícito por participante, ya que no hay un SDK que lo recopile por ti. Del lado del cliente con tu propia pila HTTP. Genera un token con POST /v3/transactions/sdk-token/ y luego llama a POST /v1/transactions/ y GET /v1/transactions/{transaction_id}/ desde el dispositivo con el encabezado X-Transaction-Token: se mantiene la misma vinculación al sujeto, y la IP y el user-agent se derivan en el servidor de la misma forma. Después, gestiona action_required por tu cuenta: Y detecta la finalización sin ningún callback en el cliente:
  • Webhooks (recomendado). transaction.status.updated se dispara cuando la acción pendiente resuelve la transacción, y travel_rule.status.updated se dispara en cada transición del intercambio; ambos incluyen lo suficiente para actualizar tus registros sin polling.
  • Polling. Vuelve a leer la transacción (/v3/ desde tu backend, o /v1/ con el token mientras sea válido): action_required vuelve a null una vez resuelta, y status / travel_rule.status contienen el resultado.
En este diseño no hay dependencias de un solo intento: el widget, la sesión de verificación y tu backend convergen en el mismo estado persistente de la transacción, por lo que siempre puedes recuperarte de una pestaña cerrada o de un webhook perdido volviendo a leer la transacción.

Webhooks

Tu backend no necesita que la aplicación le informe. Cuando una transacción enviada requiere una acción del usuario, el webhook de transacción (transaction.created o transaction.status.updated) incluye el mismo bloque action_required, de modo que tus sistemas conocen la acción pendiente aunque la aplicación se cierre antes de que termine el flujo. Una acción verification_session tiene el estado AWAITING_USER, pero una acción wallet_ownership no cambia por sí sola el estado de la transacción, así que lee action_required en lugar del estado. Las transiciones de Travel Rule emiten además travel_rule.status.updated.

Próximos pasos

Envío de transacciones

La API Create Transaction del lado del servidor y la referencia completa del payload.

Travel Rule

El intercambio gestionado entre ordenante y beneficiario, su configuración y su ciclo de vida.

Widget de propiedad de wallet

El flujo alojado para demostrar el control que sustenta las acciones wallet_ownership.

Webhooks

transaction.created, transaction.status.updated y verificación de firma.

Reglas

Acciones de las reglas, incluidas las que crean sesiones de verificación.

Estados

Qué significa AWAITING_USER en el ciclo de vida de la transacción.
Última modificación el 1 de noviembre de 2026