> ## 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.

# Prova de propriedade de carteira - Widget da Travel Rule

> Widget da Didit que prova o controle de carteiras cripto via assinatura, teste Satoshi, captura de tela ou autodeclaração, para a Travel Rule.

O widget de propriedade de carteira é uma página hospedada que comprova que um dos seus clientes controla um endereço específico de carteira cripto. Pelo TFR da UE, um VASP deve verificar a propriedade de uma carteira autocustodiada (não hospedada) antes de liberar fundos para ela ou a partir dela acima do limiar de sunrise. O widget é a forma como a Didit coleta essa prova sem que você precise criar fluxos de assinatura por conta própria.

Uma prova verificada marca a [entrada correspondente no catálogo de endereços de carteiras](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#catálogo-de-endereços-de-carteiras) como `is_ownership_verified: true`, o que permite que uma [transferência da Travel Rule](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md) vinculada saia de `UNCONFIRMED_OWNERSHIP`.

## Quando a Didit cria a sessão para você

Raramente você precisa criar uma sessão por conta própria.
Quando `auto_wallet_verification` está ativado nas suas [configurações da Travel Rule](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#configuração) (o padrão), a Didit cria automaticamente uma sessão do widget na criação da transação sempre que uma transferência exige que o usuário final comprove o controle da carteira:

* A transferência está em `UNCONFIRMED_OWNERSHIP`: a carteira da contraparte foi [declarada autocustodiada](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#carteiras-autocustodiadas-declaradas) no envio.
* A transferência é de saída e está em `COUNTERPARTY_VASP_NOT_FOUND`: nenhum VASP da contraparte pôde ser identificado, então a prova de propriedade é o caminho que resta para a conclusão.

A sessão criada automaticamente é retornada no bloco `action_required` da transação (`type: "wallet_ownership"`, com a `url` a ser aberta), e os SDKs a abrem automaticamente no [envio do lado do cliente](https://docs.didit.me/pt-br/transaction-monitoring/sdk-transaction-submission.md#ações-exigidas-do-usuário).
Transferências de entrada que a Didit cria para você (um registro de sunrise, uma consulta `TRP` de entrada ou o trecho de entrada pela via `INTERNAL`) nunca recebem uma sessão criada automaticamente, mesmo quando entram em `UNCONFIRMED_OWNERSHIP`.
Crie a sessão manualmente com `POST /v3/travel-rule/widget-session/` quando quiser verificar previamente uma carteira antes de existir qualquer transferência, quando tiver `auto_wallet_verification` desativado ou para uma dessas transferências de entrada.

## O que ele comprova

O widget oferece até quatro métodos de comprovação. Quais deles aparecem depende de como você criou a sessão e das suas [configurações da Travel Rule](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#configuração):

| Método | Como funciona | Quando é oferecido |
| - | - | - |
| `MESSAGE_SIGNING` | O cliente assina com a carteira uma string de desafio emitida pela Didit. Funciona com carteiras EVM, Solana, Bitcoin e Tron (no Bitcoin, endereços legacy, nested SegWit, native SegWit e Taproot). A Didit verifica a assinatura em relação ao endereço, e uma assinatura feita com uma conta diferente da que está sendo verificada é rejeitada com um aviso claro para trocar de conta. Contas de contrato inteligente EVM (smart wallets ERC-4337) também assinam: a Didit consulta o próprio contrato da conta, na chain declarada para a carteira, para saber se a assinatura é válida (ERC-1271). Para uma conta que ainda não foi implantada, a Didit verifica a assinatura ERC-6492 que a carteira retorna, sem implantar a conta nem enviar nenhuma transação. Contas de contrato inteligente são verificadas em todas as chains EVM compatíveis. Consulte o [contrato de verificação da assinatura de mensagem](#contrato-de-verificação-da-assinatura-de-mensagem) abaixo. As carteiras instaladas no navegador do cliente são detectadas pelos protocolos EIP-6963 e Wallet Standard, então cada extensão aparece como uma opção de assinatura própria. Parear um aplicativo de carteira móvel separado, por exemplo por QR code, ainda não está disponível. | Sempre, para as famílias de chains compatíveis. |
| `SATOSHI_TEST` | O cliente envia um valor on-chain muito pequeno da carteira para um endereço de depósito que você controla. O monitor da Didit verifica o remetente (no caso de uma conta de contrato inteligente EVM, a própria conta, inclusive quando um bundler ERC-4337 envia a transação), o valor e se o depósito foi confirmado on-chain, e conclui a comprovação automaticamente quando o depósito é confirmado. Consulte o [contrato de verificação do teste de Satoshi](#contrato-de-verificação-do-teste-de-satoshi) abaixo. | Somente quando você informa um `satoshi_deposit_address` ao criar a sessão e a chain é EVM, Bitcoin ou Solana; portanto, nunca em uma sessão criada automaticamente. |
| `SCREENSHOT` | O cliente envia uma captura de tela da carteira mostrando o endereço (JPEG/PNG/WebP, até 10 MB). Diferentemente dos outros métodos, ela nunca é verificada automaticamente: sua equipe de conformidade a analisa na fila de revisão de capturas de tela ([`GET /v3/travel-rule/wallet-proofs/`](https://docs.didit.me/pt-br/management-api/travel-rule/list-wallet-proofs)) e a aprova ou rejeita ([`POST .../decision/`](https://docs.didit.me/pt-br/management-api/travel-rule/decide-wallet-proof)), ou confirma a transferência na aba **Travel Rule** da transação no console ou via [`POST .../travel-rule/ownership/`](https://docs.didit.me/pt-br/management-api/travel-rule/confirm-ownership) (para uma transferência em `UNCONFIRMED_OWNERSHIP` ou uma transferência de entrada retida). | Somente quando `allow_screenshot_proof` é `true` nas suas configurações. |
| `SELF_DECLARATION` | O cliente declara a propriedade sem prova criptográfica, como último recurso onde a jurisdição permite. | Somente quando `allow_self_declaration` é `true` e o seu [`ownership_validation_mode`](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule#modos-de-validação-de-propriedade) aceita uma declaração para a transferência vinculada: nunca em `FULL` e, em `SIMPLIFIED`, apenas abaixo de `simplified_validation_threshold`. Iniciar uma declaração que o modo não aceita retorna `403`. |

### Contrato de verificação da assinatura de mensagem

O envio da assinatura retorna `200` com `status: "VERIFIED"` quando ela comprova o controle da carteira.
Caso contrário, retorna `400` e mantém a comprovação aberta, para que o cliente possa assinar novamente, com um destes valores de `code`:

| `code` | Significado |
| - | - |
| `SIGNATURE_INVALID` | A assinatura não comprova a carteira: outra conta ou chave assinou, ou a mensagem é diferente. No caso de uma conta de contrato inteligente, o contrato respondeu que a assinatura não é dele. |
| `SIGNATURE_UNSUPPORTED_SCHEME` | A carteira assinou em um formato que a Didit não verifica, por exemplo um multisig de Bitcoin ou uma assinatura BIP-322 completa. |
| `SIGNATURE_LOOKUP_FAILED` | A carteira pode ser uma conta de contrato inteligente, e não foi possível verificar a chain dela no momento. A comprovação nunca é verificada com essa resposta; assinar novamente faz uma nova verificação. |

### Contrato de verificação do teste de Satoshi

A verificação de um depósito do teste de Satoshi é assíncrona, porque a transação precisa ser vista on-chain e depois confirmada.
O envio do `txid` do depósito retorna `200` com `status: "PENDING"` enquanto o depósito ainda não foi confirmado on-chain; o `txid` fica armazenado, não é descartado.
Retorna `200` com `status: "VERIFIED"` quando o depósito é confirmado.
Retorna `400` com `status: "REJECTED"` para um depósito realmente incorreto: enviado de outra carteira, com um valor diferente do esperado para o endereço de depósito, anterior ao desafio, ou uma transação que já comprovou outra carteira. A comprovação é encerrada e o cliente pode iniciar uma nova enquanto a sessão ainda tiver tentativas restantes.
Um depósito que ainda não foi indexado também retorna `200` com `status: "PENDING"`, e uma consulta que falha do lado da Didit retorna `400` com o código `DEPOSIT_LOOKUP_FAILED`: nos dois casos, a comprovação continua aberta e a Didit continua verificando.
Retorna `410` se a janela do teste expirar antes de um depósito válido ser visto.

Depois que um `txid` é enviado, um job em segundo plano verifica novamente o depósito pendente a cada cinco minutos e conclui a comprovação quando a transação é confirmada, então o cliente não precisa reenviar o `txid`.
Quando você informa um `satoshi_deposit_address`, o próprio `expires_at` da sessão do widget é estendido para, no mínimo, a janela de 24 horas do teste de Satoshi, dando tempo para o depósito ser confirmado antes que a própria sessão expire.

## Fluxo

**1. Crie uma sessão do widget**

Chame `POST /v3/travel-rule/widget-session/` com a carteira que você precisa verificar. Opcionalmente, vincule-a a uma transferência para que a transferência avance automaticamente assim que a propriedade for comprovada.

**2. Abra a URL retornada**

Envie seu cliente para a `url` da resposta. Ele conclui uma prova ali, sem precisar de conta Didit nem de chave de API: o token na URL serve para autenticação.

**3. Detecte a conclusão**

Quando uma prova é verificada, o widget marca a entrada do catálogo de endereços como verificada e, se a sessão estiver vinculada a uma transferência, faz essa transferência avançar. Acompanhe `travel_rule.status.updated` (transferências vinculadas) ou consulte periodicamente o contexto do widget.

### Crie uma sessão do widget

Este exemplo vincula a sessão à [transferência autocustodiada declarada](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#carteiras-autocustodiadas-declaradas) do guia de Travel Rule, então `wallet_address` é a carteira de destino dessa transferência.
Uma sessão vinculada a uma transferência só aceita provas para a carteira da contraparte da transferência.

```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 | Descrição |
| - | - | - |
| `wallet_address` | string | O endereço da carteira a verificar. |
| `chain` | string | Identificador da blockchain do endereço (por exemplo, `ETH`, `BTC`, `SOL`). Use o mesmo token que suas transferências usam (`ETH`, não `ethereum`): o widget salva a entrada do catálogo de endereços com essa `chain`, e uma transferência em outra blockchain não corresponde a ela. A assinatura de mensagem exige uma blockchain que a Didit reconheça. |
| `holder_name` | string | Nome do titular da carteira, usado na correspondência do nome do beneficiário. |
| `vendor_data` | string | Seu identificador interno do titular, levado para a entrada do catálogo de endereços criada. |
| `transaction_id` | string (uuid) | Opcional. UUID da transação na Didit de uma transferência de Travel Rule a avançar quando a prova for verificada; um UUID desconhecido retorna `404`, e uma prova para qualquer carteira que não seja a carteira da contraparte da transferência é rejeitada com `400`. Omita para verificar uma carteira de forma independente. |
| `satoshi_deposit_address` | string | Opcional. Um endereço de depósito que você controla; informá-lo habilita o método `SATOSHI_TEST`. |
| `callback_url` | string | Opcional. Para onde o widget leva o cliente após a conclusão. |
| `expires_in_minutes` | integer | Opcional. Validade do link criado, em minutos, de `5` a `10080` (padrão `60`); é aumentada automaticamente para no mínimo a janela de 24 horas do teste de Satoshi quando você informa um `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"
}
```

Sempre abra a `url` retornada na resposta exatamente como veio; não a monte você mesmo.

### Abra a URL retornada

> [!WARNING]
>
> **Nesta versão, o widget funciona apenas na web. Abra a `url` no navegador do sistema ou em uma custom tab, nunca dentro de uma webview de um SDK mobile da Didit.**
>
> A assinatura de mensagem funciona com carteiras instaladas no navegador do cliente, que o widget detecta pelos protocolos EIP-6963 e Wallet Standard.
> Parear um app de carteira mobile separado, por exemplo por QR code, não está disponível hoje, então o widget não transfere o fluxo para outro app.
> Uma webview dentro do app, como a webview dos SDKs Web/Flutter/React Native/iOS/Android da Didit, **não consegue acessar as carteiras do navegador do cliente**, então um widget aberto ali deixa o cliente sem como assinar.
>
> No mobile, abra a `url` com o navegador do sistema operacional (`SFSafariViewController` / Chrome Custom Tabs, ou o navegador padrão) em vez de renderizá-la em uma webview. Na web, uma aba normal ou um popup funcionam bem.

### Detecte a conclusão

O widget expõe um endpoint público de leitura identificado pelo token. Consulte-o periodicamente para ver o progresso ou use webhooks quando a sessão estiver vinculada a uma transferência:

```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` deixa de ser nulo assim que uma prova é verificada.
* Cada entrada em `proofs` traz os campos de que seu método precisa: uma prova `SATOSHI_TEST` acrescenta `deposit_address`, `expected_amount` e seu próprio `expires_at`; uma prova `MESSAGE_SIGNING` acrescenta `challenge`, a string que o cliente assina.
* Uma prova `SATOSHI_TEST` pode ficar em `status: "PENDING"` enquanto seu depósito aparece on-chain sem confirmação, sem nenhuma ação necessária de você ou do cliente. Veja o [contrato de verificação do teste de Satoshi](#contrato-de-verificação-do-teste-de-satoshi) acima.
* Quando a sessão foi criada com um `transaction_id`, verificar a prova faz a transferência vinculada avançar e, em uma aplicação em produção, um webhook [`travel_rule.status.updated`](https://docs.didit.me/pt-br/integration/webhooks#tipos-de-evento) é disparado.
  Prefira o webhook à consulta periódica.
  Uma consulta `TRP` recebida é aprovada de volta para o remetente e passa para `COMPLETED` assim que essa aprovação é enviada.
  Qualquer outra transferência em `UNCONFIRMED_OWNERSHIP` (ou uma recebida em `ON_HOLD`) passa para `COMPLETED`, ou para `COUNTERPARTY_MISMATCHED_DATA` quando o nome do beneficiário não corresponde ao titular da carteira, e um depósito sunrise liquidado que seja concluído passa para `FINISHED`.
  Uma transferência enviada em `COUNTERPARTY_VASP_NOT_FOUND` passa para `COMPLETED`.
* Uma prova `SCREENSHOT` mantém `status: "PENDING"` neste contexto mesmo depois que você confirma a propriedade.
  Você a aprova com [`POST .../travel-rule/ownership/`](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule#confirmação-de-propriedade-da-carteira), que só aceita uma transferência aguardando confirmação de propriedade.
  Por isso, uma captura de tela não tem como ser aprovada em uma sessão sem transferência vinculada, nem em uma sessão vinculada a uma transferência enviada em `COUNTERPARTY_VASP_NOT_FOUND`: desative `allow_screenshot_proof` se essas sessões não puderem oferecê-la.
* Depois de uma prova rejeitada, a sessão aceita uma nova tentativa enquanto restarem tentativas.
  `max_attempts`, `attempts_used` e `attempts_remaining` informam a contagem, uma prova rejeitada traz `rejection_reason` e, quando `attempts_remaining` é `0`, iniciar outra prova retorna `409` e você precisa criar uma nova sessão.
* O endpoint de contexto retorna `404` para um token desconhecido e `410` quando a sessão expirou sem ser concluída.

> [!NOTE]
>
> Não há dependências que só possam ser executadas uma vez aqui: o widget e o seu backend convergem para o mesmo estado persistente (a entrada verificada do catálogo de endereços e o status da transferência), então a perda de um webhook ou o fechamento de uma aba são sempre contornáveis ao consultar novamente a transferência ou o contexto do widget.

## Localização e identidade visual

O widget é exibido em **54 idiomas**, conforme o idioma indicado no caminho do link (ou o padrão do navegador).

Quando a marca própria está ativada para a sua aplicação, o widget herda automaticamente a identidade visual do seu fluxo de verificação: logotipo, cores da marca, estilo de botões e painéis, fonte personalizada, favicon e o nome público do seu app no título.
Não há configuração separada: a mesma personalização que você definiu para as sessões de KYC vale aqui.

Um usuário que volta a um link já concluído vê a tela de conclusão novamente; se você informou uma `callback_url`, o widget o redireciona para ela.
Links expirados exibem uma tela de expiração com a sua identidade visual, incentivando o usuário a solicitar um novo link.
Crie uma nova sessão para ele com `POST /v3/travel-rule/widget-session/`: reenviar uma transferência com `action: "resend"` não cria uma nova sessão.

## Próximos passos

> [card] [Travel Rule](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md)
>
> O ciclo de vida completo da troca, os canais, as configurações e os fluxos de recebimento.

> [card] [Agenda de endereços de carteiras](https://docs.didit.me/pt-br/transaction-monitoring/travel-rule.md#catálogo-de-endereços-de-carteiras)
>
> Registre as carteiras que você controla para que as transferências recebidas sejam associadas a elas.

> [card] [Webhooks](https://docs.didit.me/pt-br/integration/webhooks.md)
>
> Assine travel\_rule.status.updated para reagir quando uma prova fizer com que uma transferência avance.

> [card] [SDKs nativos](https://docs.didit.me/pt-br/integration/native-sdks/overview.md)
>
> Abra a URL do widget no navegador do sistema, não na webview do SDK.


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