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_requiredde tipowallet_ownershipy 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ónverification_sessionrequerida 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 conPOST /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/.
Envío desde el dispositivo
Los SDK envían una solicitud aPOST /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:
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: elpaymentMethod.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_verificationestá 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 enaction_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 aCOUNTERPARTY_MISMATCHED_DATAsi el nombre no coincide con el titular de la wallet en la libreta de direcciones.
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.externalUserIddel payload se ignora y se sustituye por elvendor_datadel 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.
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.
/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 bloqueaction_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
useNativeWalletOwnershipestrue(el valor por defecto), y recurre, si no es posible, aSFSafariViewController/ 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.
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.
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
Cuandoaction_required.type es verification_session, lee el bloque de result.actionRequired y pásalo a tu integración de verificación de Didit existente:
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.SDK web
submitTransaction ya está disponible en el singleton del SDK web:
{ 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.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 conPOST /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.updatedse dispara cuando la acción pendiente resuelve la transacción, ytravel_rule.status.updatedse 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_requiredvuelve anulluna vez resuelta, ystatus/travel_rule.statuscontienen el resultado.
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.