> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget de propiedad de wallet: prueba de control

> El widget alojado de Didit prueba que un cliente controla una wallet con firma, test de Satoshi, captura o autodeclaración, para la Travel Rule.

El widget de propiedad de wallet es una página alojada que demuestra que uno de tus clientes controla una dirección de wallet de criptomonedas concreta. Según el TFR de la UE, un VASP debe verificar la propiedad de una wallet sin custodia (no alojada) antes de liberar fondos hacia ella o desde ella por encima del umbral de sunrise; el widget es la forma en que Didit recopila esa prueba sin que tengas que crear tú mismo los flujos de firma.

Una prueba verificada marca la [entrada correspondiente de la libreta de direcciones de wallets](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#libreta-de-direcciones-de-wallets) como `is_ownership_verified: true`, lo que permite que una [transferencia de Travel Rule](https://docs.didit.me/es/transaction-monitoring/travel-rule.md) vinculada salga del estado `UNCONFIRMED_OWNERSHIP`.

## Cuándo la genera Didit por ti

Rara vez necesitas generar una sesión tú mismo.
Cuando `auto_wallet_verification` está activado en tu [configuración de Travel Rule](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#configuración) (el valor predeterminado), Didit genera automáticamente una sesión del widget al crear la transacción siempre que una transferencia requiera que el usuario final demuestre el control de la wallet:

* La transferencia está en `UNCONFIRMED_OWNERSHIP`: la wallet de la contraparte se [declaró sin custodia](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#wallets-sin-custodia-declaradas) en el envío.
* La transferencia es saliente y está en `COUNTERPARTY_VASP_NOT_FOUND`: no se pudo identificar ningún VASP de contraparte, por lo que la prueba de propiedad es la vía que queda para completarla.

La sesión generada automáticamente se devuelve en el bloque `action_required` de la transacción (`type: "wallet_ownership"`, con la `url` que hay que abrir), y los SDK la abren automáticamente en el [envío desde el cliente](https://docs.didit.me/es/transaction-monitoring/sdk-transaction-submission.md#acciones-requeridas-del-usuario).
Las transferencias entrantes que Didit crea por ti (un registro de sunrise, una consulta `TRP` entrante o el tramo entrante por la vía `INTERNAL`) nunca reciben una sesión generada automáticamente, aunque terminen en `UNCONFIRMED_OWNERSHIP`.
Genera la sesión manualmente con `POST /v3/travel-rule/widget-session/` cuando quieras verificar una wallet de antemano, antes de que exista ninguna transferencia, cuando tengas `auto_wallet_verification` desactivado o para una de esas transferencias entrantes.

## Qué demuestra

El widget ofrece hasta cuatro métodos de prueba. Los que aparecen dependen de cómo generaste la sesión y de tu [configuración de Travel Rule](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#configuración):

| Método | Cómo funciona | Cuándo se ofrece |
| - | - | - |
| `MESSAGE_SIGNING` | El cliente firma con su wallet una cadena de desafío emitida por Didit. Admite wallets EVM, Solana, Bitcoin y Tron (en Bitcoin, direcciones legacy, nested SegWit, native SegWit y Taproot); Didit verifica la firma frente a la dirección, y si se firma con una cuenta distinta de la que se está verificando, la firma se rechaza con un aviso claro para cambiar de cuenta. Las cuentas de contrato inteligente EVM (smart wallets ERC-4337) también firman: Didit pregunta al propio contrato de la cuenta, en la cadena en la que está declarada la wallet, si la firma es válida (ERC-1271), y para una cuenta que todavía no está desplegada comprueba la firma ERC-6492 que devuelve la wallet, sin desplegar la cuenta ni enviar ninguna transacción. Las cuentas de contrato inteligente se comprueban en todas las cadenas EVM compatibles. Consulta [Contrato de verificación de la firma de mensajes](#contrato-de-verificación-de-la-firma-de-mensajes) más abajo. Las wallets instaladas en el navegador del cliente se detectan mediante los protocolos EIP-6963 y Wallet Standard, por lo que cada extensión aparece como una opción de firma independiente. Por ahora no es posible vincular una app de wallet móvil independiente, por ejemplo mediante código QR. | Siempre, para las familias de cadenas compatibles. |
| `SATOSHI_TEST` | El cliente envía una cantidad on-chain mínima desde la wallet a una dirección de depósito que tú controlas; el monitor de Didit comprueba el remitente (en una cuenta de contrato inteligente EVM, la propia cuenta, también cuando un bundler ERC-4337 envía la transacción), el importe y que el depósito esté confirmado on-chain, y completa la prueba automáticamente en cuanto se confirma. Consulta [Contrato de verificación del test de satoshis](#contrato-de-verificación-del-test-de-satoshis) más abajo. | Solo cuando pasas una `satoshi_deposit_address` al generar la sesión y la cadena es EVM, Bitcoin o Solana, por lo que nunca en una sesión generada automáticamente. |
| `SCREENSHOT` | El cliente sube una captura de pantalla de la wallet en la que se ve la dirección (JPEG/PNG/WebP, hasta 10 MB). A diferencia de los demás métodos, nunca se verifica automáticamente: tu equipo de cumplimiento la revisa desde la cola de revisión de capturas de pantalla ([`GET /v3/travel-rule/wallet-proofs/`](https://docs.didit.me/es/management-api/travel-rule/list-wallet-proofs)) y la aprueba o la rechaza ([`POST .../decision/`](https://docs.didit.me/es/management-api/travel-rule/decide-wallet-proof)), o confirma la transferencia desde la pestaña **Travel Rule** de la transacción en la consola o mediante [`POST .../travel-rule/ownership/`](https://docs.didit.me/es/management-api/travel-rule/confirm-ownership) (para una transferencia en `UNCONFIRMED_OWNERSHIP` o una transferencia entrante retenida). | Solo cuando `allow_screenshot_proof` es `true` en tu configuración. |
| `SELF_DECLARATION` | El cliente declara la propiedad sin prueba criptográfica: un último recurso cuando la jurisdicción lo admite. | Solo cuando `allow_self_declaration` es `true` y tu [`ownership_validation_mode`](https://docs.didit.me/es/transaction-monitoring/travel-rule#modos-de-validación-de-la-propiedad) acepta una declaración para la transferencia vinculada: nunca en `FULL`, y en `SIMPLIFIED` solo por debajo de `simplified_validation_threshold`. Iniciar una declaración que el modo no acepta devuelve `403`. |

### Contrato de verificación de la firma de mensajes

Enviar la firma devuelve `200` con `status: "VERIFIED"` cuando demuestra el control de la wallet.
Devuelve `400` y deja la prueba abierta, para que el cliente pueda volver a firmar, con uno de estos valores de `code`:

| `code` | Significado |
| - | - |
| `SIGNATURE_INVALID` | La firma no demuestra el control de la wallet: firmó otra cuenta u otra clave, o el mensaje es distinto. En una cuenta de contrato inteligente, su contrato respondió que la firma no es suya. |
| `SIGNATURE_UNSUPPORTED_SCHEME` | La wallet firmó en un formato que Didit no verifica, por ejemplo una multisig de Bitcoin o una firma completa BIP-322. |
| `SIGNATURE_LOOKUP_FAILED` | Es posible que la wallet sea una cuenta de contrato inteligente y que su cadena no se haya podido comprobar en este momento. La prueba nunca se verifica con esta respuesta; al volver a firmar se comprueba de nuevo. |

### Contrato de verificación del test de satoshis

La verificación de un depósito del test de satoshis es asíncrona, porque la transacción tiene que verse on-chain y después confirmarse.
Enviar el `txid` del depósito devuelve `200` con `status: "PENDING"` mientras el depósito aún no esté confirmado on-chain; el `txid` se conserva, no se descarta.
Devuelve `200` con `status: "VERIFIED"` cuando el depósito se confirma.
Devuelve `400` con `status: "REJECTED"` para un depósito realmente incorrecto: enviado desde otra wallet, que no envía el importe esperado a la dirección de depósito, anterior al desafío o que ya se utilizó para demostrar la propiedad de otra wallet. La prueba se cierra y el cliente puede iniciar una nueva mientras a la sesión le queden intentos.
Un depósito que todavía no está indexado también devuelve `200` con `status: "PENDING"`, y una consulta que falla por parte de Didit devuelve `400` con el código `DEPOSIT_LOOKUP_FAILED`: en ambos casos la prueba sigue abierta y Didit sigue comprobándola.
Devuelve `410` si la ventana del test expira antes de que se detecte un depósito válido.

Una vez enviado un `txid`, un proceso en segundo plano vuelve a comprobar el depósito pendiente cada cinco minutos y completa la prueba cuando la transacción se confirma, por lo que el cliente no necesita volver a enviar el `txid`.
Cuando pasas una `satoshi_deposit_address`, el propio `expires_at` de la sesión del widget se amplía como mínimo a la ventana de 24 horas del test de satoshis, lo que da tiempo a que el depósito se confirme antes de que expire la sesión.

## Flujo

**1. Crea una sesión del widget**

Llama a `POST /v3/travel-rule/widget-session/` con la wallet que necesitas verificar. Opcionalmente, vincula la sesión a una transferencia para que esta avance automáticamente en cuanto se demuestre la propiedad.

**2. Abre la URL devuelta**

Envía a tu cliente a la `url` de la respuesta. Allí completa una prueba sin necesidad de una cuenta de Didit ni de una clave de API: el token de la URL es la autenticación.

**3. Detecta la finalización**

Cuando se verifica una prueba, el widget marca como verificada la entrada de la libreta de direcciones y, si la sesión está vinculada a una transferencia, la hace avanzar. Espera el evento `travel_rule.status.updated` (transferencias vinculadas) o consulta periódicamente el contexto del widget.

### Crea una sesión del widget

Este ejemplo vincula la sesión a la [transferencia declarada a una wallet sin custodia](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#wallets-sin-custodia-declaradas) de la guía de Travel Rule, por lo que `wallet_address` es la wallet de destino de esa transferencia.
Una sesión vinculada a una transferencia solo acepta pruebas para la wallet de la contraparte de la transferencia.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
    "chain": "ETH",
    "holder_name": "Jane Doe",
    "vendor_data": "user-042",
    "transaction_id": "99999999-aaaa-bbbb-cccc-dddddddddddd",
    "satoshi_deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "callback_url": "https://yourapp.example/wallet-verified",
    "expires_in_minutes": 60
  }'
```

| Campo | Tipo | Descripción |
| - | - | - |
| `wallet_address` | string | La dirección de la wallet que se va a verificar. |
| `chain` | string | Identificador de la cadena de la dirección (por ejemplo, `ETH`, `BTC`, `SOL`). Usa el mismo token que llevan tus transferencias (`ETH`, no `ethereum`): el widget guarda la entrada de la libreta de direcciones con esta `chain`, y una transferencia en otra cadena no coincide con ella. La firma de mensajes requiere una cadena que Didit reconozca. |
| `holder_name` | string | Nombre del titular de la wallet, que se usa para la coincidencia del nombre del beneficiario. |
| `vendor_data` | string | Tu identificador interno del titular, que se traslada a la entrada de la libreta de direcciones creada. |
| `transaction_id` | string (uuid) | Opcional. UUID de transacción de Didit de una transferencia de Travel Rule que debe avanzar cuando se verifique la prueba; un UUID desconocido devuelve `404`, y una prueba para cualquier wallet distinta de la wallet de la contraparte de la transferencia se rechaza con `400`. Omítelo para verificar una wallet de forma independiente. |
| `satoshi_deposit_address` | string | Opcional. Una dirección de depósito que controlas; al indicarla se habilita el método `SATOSHI_TEST`. |
| `callback_url` | string | Opcional. Adónde devuelve el widget al cliente tras completar el proceso. |
| `expires_in_minutes` | integer | Opcional. Vigencia del enlace creado en minutos, de `5` a `10080` (valor predeterminado `60`); se amplía automáticamente hasta al menos la ventana de 24 horas del test de Satoshi cuando pasas una `satoshi_deposit_address`. |

```json theme={null}
{
  "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb",
  "token": "3s9x...urlsafe",
  "url": "https://verify.didit.me/wallet-ownership/3s9x...urlsafe",
  "expires_at": "2026-07-07T13:00:00Z"
}
```

Abre siempre la `url` devuelta en la respuesta tal cual, sin construirla tú.

### Abre la URL devuelta

> [!WARNING]
>
> **En esta versión, el widget solo funciona en la web. Abre la `url` en el navegador del sistema o en una custom tab, nunca dentro de un webview de un SDK móvil de Didit.**
>
> La firma de mensajes funciona con las wallets instaladas en el navegador del cliente, que el widget detecta mediante los protocolos EIP-6963 y Wallet Standard.
> Vincular una aplicación de wallet móvil independiente, por ejemplo mediante código QR, no está disponible actualmente, por lo que el widget no pasa a otra aplicación.
> Un webview dentro de la aplicación, como el webview de los SDK de Didit para Web, Flutter, React Native, iOS o Android, **no puede acceder a las wallets del navegador del cliente**, por lo que un widget abierto allí deja al cliente sin forma de firmar.
>
> En dispositivos móviles, abre la `url` con el navegador del sistema operativo (`SFSafariViewController` / Chrome Custom Tabs, o el navegador predeterminado) en lugar de mostrarla en un webview. En la web, basta con una pestaña normal o una ventana emergente.

### Detecta la finalización

El widget expone un endpoint público de lectura identificado por el token: consúltalo periódicamente para ver el progreso, o confía en los webhooks cuando la sesión esté vinculada a una transferencia:

```bash theme={null}
curl https://verification.didit.me/v3/travel-rule/widget/{token}/
```

```json theme={null}
{
  "vasp_name": "Origin CASP SL",
  "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
  "chain": "ETH",
  "holder_name": "Jane Doe",
  "allowed_methods": ["MESSAGE_SIGNING", "SCREENSHOT", "SATOSHI_TEST", "SELF_DECLARATION"],
  "transfer_status": "UNCONFIRMED_OWNERSHIP",
  "callback_url": "https://yourapp.example/wallet-verified",
  "expires_at": "2026-07-07T13:00:00Z",
  "completed_at": null,
  "proofs": [
    {
      "proof_id": "88888888-9999-aaaa-bbbb-cccccccccccc",
      "method": "SATOSHI_TEST",
      "status": "PENDING",
      "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
      "chain": "ETH",
      "deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "expected_amount": "0.000042817305521963",
      "expires_at": "2026-07-07T13:05:00.412731+00:00",
      "created_at": "2026-07-06T13:05:00.412731Z"
    }
  ],
  "customization": null
}
```

* `completed_at` deja de ser nulo en cuanto se verifica una prueba.
* Cada entrada de `proofs` incluye los campos que necesita su método: una prueba `SATOSHI_TEST` añade `deposit_address`, `expected_amount` y su propio `expires_at`; una prueba `MESSAGE_SIGNING` añade `challenge`, la cadena que firma el cliente.
* Una prueba `SATOSHI_TEST` puede permanecer en `status: "PENDING"` mientras se detecta su depósito on-chain pero sin confirmar, sin que tú ni el cliente tengan que hacer nada: consulta el [contrato de verificación del test de Satoshi](#contrato-de-verificación-del-test-de-satoshis) más arriba.
* Cuando la sesión se creó con un `transaction_id`, verificar la prueba hace avanzar la transferencia vinculada y, en una aplicación en producción, se dispara un webhook [`travel_rule.status.updated`](https://docs.didit.me/es/integration/webhooks#tipos-de-eventos).
  Prefiere el webhook a la consulta periódica.
  Una consulta `TRP` entrante se responde como aprobada al remitente y pasa a `COMPLETED` en cuanto se entrega esa aprobación.
  Cualquier otra transferencia en `UNCONFIRMED_OWNERSHIP` (o una entrante en `ON_HOLD`) pasa a `COMPLETED`, o a `COUNTERPARTY_MISMATCHED_DATA` cuando el nombre del beneficiario no coincide con el titular de la wallet, y un depósito sunrise liquidado que se completa continúa hasta `FINISHED`.
  Una transferencia saliente en `COUNTERPARTY_VASP_NOT_FOUND` pasa a `COMPLETED`.
* Una prueba `SCREENSHOT` mantiene `status: "PENDING"` en este contexto incluso después de que confirmes la propiedad.
  La apruebas con [`POST .../travel-rule/ownership/`](https://docs.didit.me/es/transaction-monitoring/travel-rule#confirmar-la-propiedad-de-la-wallet), que solo acepta una transferencia pendiente de confirmación de propiedad.
  Por tanto, una captura de pantalla no tiene vía de aprobación en una sesión sin transferencia vinculada, ni en una sesión vinculada a una transferencia saliente en `COUNTERPARTY_VASP_NOT_FOUND`: desactiva `allow_screenshot_proof` si esas sesiones no deben ofrecerla.
* Tras una prueba rechazada, la sesión admite un nuevo intento mientras queden intentos.
  `max_attempts`, `attempts_used` y `attempts_remaining` indican el recuento, una prueba rechazada incluye `rejection_reason` y, cuando `attempts_remaining` es `0`, iniciar otra prueba responde `409` y debes crear una nueva sesión.
* El endpoint de contexto devuelve `404` para un token desconocido y `410` cuando la sesión ha expirado sin completarse.

> [!NOTE]
>
> Aquí no hay dependencias de ejecución única: tanto el widget como tu backend convergen en el mismo estado persistente (la entrada verificada de la libreta de direcciones y el estado de la transferencia), por lo que un webhook perdido o una pestaña cerrada siempre se pueden recuperar volviendo a leer la transferencia o el contexto del widget.

## Localización y personalización de marca

El widget se muestra en **54 idiomas**, según la ruta de idioma del enlace (o el idioma predeterminado del navegador).

Cuando la marca blanca está activada para tu aplicación, el widget hereda automáticamente la marca de tu flujo de verificación: logotipo, colores de marca, estilo de botones y paneles, fuente personalizada, favicon y el nombre público de tu aplicación en el título.
No hace falta ninguna configuración aparte: la misma personalización que configuras para las sesiones de KYC se aplica aquí.

Un usuario que vuelve a un enlace ya completado ve de nuevo la pantalla de finalización; si pasaste una `callback_url`, el widget lo redirige allí.
Los enlaces expirados muestran una pantalla de caducidad con tu marca que le pide al usuario solicitar un nuevo enlace.
Crea una nueva sesión para el usuario con `POST /v3/travel-rule/widget-session/`: reenviar una transferencia con `action: "resend"` no crea ninguna.

## Próximos pasos

> [card] [Travel Rule](https://docs.didit.me/es/transaction-monitoring/travel-rule.md)
>
> El ciclo de vida completo del intercambio, los canales, la configuración y los flujos de recepción.

> [card] [Libreta de direcciones de wallets](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#libreta-de-direcciones-de-wallets)
>
> Registra las wallets que controlas para que las transferencias entrantes se vinculen a ellas.

> [card] [Webhooks](https://docs.didit.me/es/integration/webhooks.md)
>
> Suscríbete a travel\_rule.status.updated para reaccionar cuando una prueba haga que una transferencia avance.

> [card] [SDK nativos](https://docs.didit.me/es/integration/native-sdks/overview.md)
>
> Abre la URL del widget con el navegador del sistema, no con el webview del SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.