Before Withdrawal: Initiate outbound data exchange

Learn how to submit compliant and secure before on-chain outgoing transfer.

Use this flow when your user is sending virtual assets from their wallet to another entity, and all of the following is true:

  • Your user is the originator of the transfer.
  • You are initiating the withdrawal.
  • Blockchain transaction has not been submitted yet.
  • You need to exchange Travel Rule data with the beneficiary VASP before the transfer goes on-chain.

In this scenario, your organization acts as the originating VASP.

👍

Tip

How processing of outbound data exchange works

Before sending funds on-chain, you need to create a Travel Rule data exchange transaction. This process includes the following steps:

  1. You need to send the required transfer details to Sumsub. Your request should include:
    • Information about the originator.
    • Information about the expected beneficiary and, if known, the beneficiary VASP.
    • Originator and counterparty wallet addresses
    • Transfer metadata, including amount, asset, and direction.
  2. Once you have sent all the data, Sumsub attempts to perform beneficiary VASP attribution to identify the beneficiary VASP.

    If the beneficiary VASP is identified, Sumsub asks them to:

    • Confirm that the destination wallet belongs to them.
    • Review the transfer details.
    • Confirm or reject the beneficiary information, depending on the protocol and your settings.

    If the beneficiary VASP is not identified, Sumsub generates a verification link that you may share with your user to clarify whether the destination wallet is hosted or unhosted.

    📘

    Note

    If no hosted VASP is confirmed, then the related flow is applied with the Travel Rule: Unhosted wallet verification rule.

  3. After the data exchange is completed and approved review outcome, submit the blockchain transaction. After the blockchain transaction is confirmed, update the Travel Rule record with the blockchain transaction ID to complete the flow.

Initiate outbound data exchange transaction

The following is a sequence of steps to be taken to initiate Before Withdrawal data exchange transaction.

Step 1: Enable required rules

To apply the Travel Rule solution to the before on-chain outgoing transfer, do the following:

  1. In the Dashboard, open the Rules Library.
  2. Select the Travel Rule bundle and install the rules.

We recommend installing all the rules available in the bundle, as it is the quickest and easiest way to cover all of the check steps.

🚧

Note

The rules you install remain in Test mode until you activate them.

Step 2: Configure Travel Rule settings

In the Dashboard, navigate to Transactions and travel rule -> Settings -> Travel Rule . Review the configuration used for outbound transfers. At minimum, configure:

  • Counterparty confirmation timeout.
  • Which originator and beneficiary identity fields may be shared with the counterparty VASP.

You can also select the Activate TR SDK on transaction checkbox to generate a verification link that you can share with your users to confirm details about the recipient wallet, including its type (hosted or unhosted), with the option to select the VASP (if hosted).

📘

Note

For more information on Travel Rule settings, refer to this article.

Step 3: Generate an app token

Once you have installed and enabled the rules, you will need to generate an app token to sign your API calls. For more information on how to generate a token, refer to this article.

Step 4: Create Travel Rule data exchange transaction

After completing the setup of the desired conditions and generating the token, you will be able to create a Travel Rule data exchange transaction before sending the withdrawal on-chain.

📘

Note

See the protocol data requirements here.

To create the Travel Rule data exchange transaction, you can use any of the following API methods:

For this flow, set the transaction direction to out.

The examples below show the request body and Content-Type header. Add your authentication and request-signing headers according to your Sumsub integration setup.

curl -X POST \
  'https://api.sumsub.com/resources/applicants/67a0ec0b9aa0951851d627ef/kyt/txns/-/data' \
  -H 'Content-Type: application/x-ndjson' \
  -d $'{
        "txnId": "b4xdq4qjh5qpo06r8cpunc",
        "type": "travelRule",
        "applicant": {
          "type": "individual",
          "nameType": "birthName",
          "externalUserId": "gh5l2s8ykab1asu2",
          "dob": "1992-05-08",
          "placeOfBirth": "Paris",
          "address": {
            "country": "FRA",
            "town": "Paris",
            "postCode": "75001",
            "street": "Rue de Rivoli",
            "buildingNumber": "1"
          },
          "idDoc": {
            "number": "FR42234089",
            "country": "FRA",
            "idDocType": "PASSPORT",
            "registrationAuthority": "Ille-de-France 01"
          },
          "residenceCountry": "FRA",
          "paymentMethod": {
            "type": "crypto",
            "accountId": "0xa79e8726DaF031f753477C79653d0d56AA0D5DF6"
          },
          "firstName": "John",
          "firstNameEn": "John",
          "lastName": "Posek",
          "lastNameEn": "Posek"
        },
        "counterparty": {
          "externalUserId": "uwccpr7tp4kjbontf",
          "nameType": "birthName",
          "type": "individual",
          "dob": "1991-04-07",
          "placeOfBirth": "Berlin, Germany",
          "address": {
            "country": "DEU",
            "town": "Berlin",
            "postCode": "10115",
            "street": "Chauseestr.",
            "buildingNumber": "60"
          },
          "firstName": "Jack",
          "firstNameEn": "Jack",
          "lastName": "Posek",
          "lastNameEn": "Posek",
          "idDoc": {
            "number": "DE42234089",
            "country": "DEU",
            "idDocType": "PASSPORT",
            "registrationAuthority": "BerlinMitte"
          },
          "residenceCountry": "DEU",
          "paymentMethod": {
            "type": "crypto",
            "accountId": "bc1q080rkmk3kj86pxvf5nkxecdrw6nrx3zzy9xl7q",
            "memo": "83927461"
          },
          "institutionInfo": {
            "internalId": "645a5a60294c3b043c84594f"
          }
        },
        "info": {
          "direction": "out",
          "amount": 150.0,
          "amountInDefaultCurrency": 127.99,
          "defaultCurrencyCode": "EUR",
          "currencyCode": "USDT",
          "paymentDetails": "Private transfer",
          "currencyType": "crypto",
          "cryptoParams": {
            "cryptoChain": "ETH",
            "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
          }
        },
        "props": {
          "dailyOutLimit": "10000",
          "customProperty": "Custom value that can be used in rules"
        },
        "zoneId": "UTC+1",
        "txnDate": "2025-01-30 11:41:55+0000"
      }'
curl -X POST \
  'https://api.sumsub.com/resources/kyt/misc/txns/import' \
  -H 'Content-Type: application/x-ndjson' \
  -d $'{
        "txnId": "b4xdq4qjh5qpo06r8cpunc",
        "type": "travelRule",
        "counterparty": {
          "externalUserId": "uwccpr7tp4kjbontf",
          "nameType": "birthName",
          "type": "individual",
          "dob": "1991-04-07",
          "placeOfBirth": "Berlin, Germany",
          "address": {
            "country": "DEU",
            "town": "Berlin",
            "postCode": "10115",
            "street": "Chauseestr.",
            "buildingNumber": "60"
          },
          "firstName": "Jack",
          "firstNameEn": "Jack",
          "lastName": "Posek",
          "lastNameEn": "Posek",
          "idDoc": {
            "number": "DE42234089",
            "country": "DEU",
            "idDocType": "PASSPORT",
            "registrationAuthority": "BerlinMitte"
          },
          "residenceCountry": "DEU",
          "paymentMethod": {
            "type": "crypto",
            "accountId": "bc1q080rkmk3kj86pxvf5nkxecdrw6nrx3zzy9xl7q",
            "memo": "83927461"
          },
          "institutionInfo": {
            "internalId": "645a5a60294c3b043c84594f"
          }
        },
        "info": {
          "direction": "out",
          "amount": 150,
          "amountInDefaultCurrency": 127.99,
          "defaultCurrencyCode": "EUR",
          "currencyCode": "USDT",
          "paymentDetails": "Private transfer",
          "currencyType": "crypto",
          "cryptoParams": {
            "cryptoChain": "ETH",
            "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
          }
        },
        "props": {
          "dailyOutLimit": "10000",
          "customProperty": "Custom value that can be used in rules"
        },
        "zoneId": "UTC+1",
        "txnDate": "2025-01-30 11:41:55+0000"
      }\n{
        "txnId": "2n8ezlub4n2jeua6u",
        "type": "travelRule",
        "counterparty": {
          "externalUserId": "q5u2x5ldkio1adtjj8",
          "fullName": "Jack Posek Ltd.",
          "leiCode": "K1X8Y3F454930012",
          "nameType": "legalName",
          "type": "company",
          "companyType": "limitedLiabilityCompany",
          "licenseNumber": "987654321",
          "address": {
            "country": "DEU",
            "town": "Berlin",
            "postCode": "10115",
            "street": "Chauseestr.",
            "buildingNumber": "60"
          },
          "ceo": {
            "firstName": "Jack",
            "firstNameEn": "Jack",
            "lastName": "Posek",
            "lastNameEn": "Posek",
            "nameType": "birthName"
          },
          "paymentMethod": {
            "type": "crypto",
            "accountId": "bc1q080rkmk3kj86pxvf5nkxecdrw6nrx3zzy9xl7q"
          },
          "residenceCountry": "DEU",
          "institutionInfo": {
            "internalId": "643c2b42f7bd5560a2f1c11e"
          },
          "registrationNumber": "DE123456789"
        },
        "info": {
          "direction": "out",
          "amount": 150,
          "amountInDefaultCurrency": 127.99,
          "defaultCurrencyCode": "EUR",
          "currencyCode": "DAI",
          "paymentDetails": "Private transfer",
          "currencyType": "crypto",
          "cryptoParams": {
            "cryptoChain": "ETH",
            "contractAddress": "0x6b175474e89094c44da98b954eedeac495271d0f"
          }
        },
        "props": {
          "dailyOutLimit": "10000",
          "customProperty": "Custom value that can be used in rules"
        },
        "zoneId": "UTC+1",
        "txnDate": "2025-12-19 15:53:00+0100"
      }\n{
        "txnId": "bhghv80bvskwne7h",
        "type": "travelRule",
        "counterparty": {
          "externalUserId": "ee66xkf8xkrw7scepn",
          "type": "individual",
          "address": {
            "country": "DEU",
            "street": "Chauseestr.",
            "town": "Berlin",
            "postCode": "10115",
            "buildingNumber": "60"
          },
          "paymentMethod": {
            "type": "unhostedWallet",
            "accountId": "bc1q080rkmk3kj86pxvf5nkxecdrw6nrx3zzy9xl7q"
          },
          "firstName": "Jack",
          "firstNameEn": "Jack",
          "lastName": "Posek",
          "lastNameEn": "Posek",
          "nameType": "birthName",
          "dob": "1991-04-07",
          "placeOfBirth": "Berlin, Germany",
          "idDoc": {
            "number": "DE42234089",
            "idDocType": "PASSPORT",
            "country": "DEU",
            "registrationAuthority": "BerlinMitte"
          },
          "residenceCountry": "DEU"
        },
        "info": {
          "direction": "out",
          "amount": 150,
          "amountInDefaultCurrency": 127.99,
          "defaultCurrencyCode": "EUR",
          "currencyCode": "USDT",
          "paymentDetails": "Private transfer",
          "currencyType": "crypto",
          "cryptoParams": {
            "cryptoChain": "ETH",
            "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
          }
        },
        "props": {
          "dailyOutLimit": "10000",
          "customProperty": "Custom value that can be used in rules"
        },
        "zoneId": "UTC+1",
        "txnDate": "2025-12-19 17:13:00+0100"
      }'

After the transaction is created, you may receive an initial applicantKytTxnCreated webhook indicating that the Travel Rule transaction has been registered and entered processing.

📘

Note

After submission, you can track the Travel Rule transaction statuses in the Dashboard. To do so, navigate to the Transactions and Travel Rule -> Transactions.

Step 5: Receive webhook notifications

After submission, Sumsub notifies you about the review outcome through one of the following webhooks:

📘

Note

Travel Rule transaction statuses are separate from the review outcomes produced by your rules.

Based on your installed rules and the beneficiary VASP response, the outbound data exchange transaction can receive one of the following review outcomes: approved, rejected, or put on hold.

In practice:

  • Travel Rule status tells you what happened in the data exchange.
  • Review outcome tells you whether your organization should proceed, block, or manually review the withdrawal

applicantKytTxnCreated

If you receive applicantKytTxnCreated, the transaction has been created and entered Travel Rule processing.

{
  "applicantType" : "individual",
  "sandboxMode" : false,
  "externalUserId" : "customExternalUserId",
  "type" : "applicantKytTxnCreated",
  "reviewStatus" : "init",
  "createdAt" : "2025-11-17 17:26:49+0000",
  "createdAtMs" : "2025-11-17 17:26:49.676",
  "clientId" : "coolClientId",
  "kytTxnId" : "691b5ad93f7d5f23100611ca",
  "kytDataTxnId" : "9682adb6-b2cc-429c-acae-f36312c34a95",
  "kytTxnType" : "travelRule"
}

applicantKytTxnApproved

If you receive applicantKytTxnApproved, the transfer passed your configured checks and you may proceed to the blockchain step.

{
  "applicantId": "634829375766b80001a40152",
  "applicantType": "individual",
  "correlationId": "f24f6616020245053139a6537303a251",
  "sandboxMode": false,
  "externalUserId": "customExternalUserId",
  "type": "applicantKytTxnApproved",
  "reviewResult": {
    "reviewAnswer": "GREEN"
  },
  "reviewStatus": "completed",
  "createdAt": "2025-01-30 11:41:55+0000",
  "createdAtMs": "2025-01-30 11:41:55+0000",
  "clientId": "coolClientId",
  "kytTxnId": "64a7dc05fbf57c624afcb72d",
  "kytDataTxnId": "b4xdq4qjh5qpo06r8cpunc",
  "kytTxnType": "travelRule"
}
🚧

Attention

Submit the withdrawal on-chain only after:

  • Travel Rule data exchange has been completed for the transaction, or Travel Rule was intentionally marked notApplicable by your configuration.
  • Transaction has passed your rules and compliance review.

applicantKytOnHold

If you receive applicantKytOnHold, the transfer requires manual review by the dedicated compliance officer.

{
  "applicantId": "634829375766b80001a40152",
  "applicantType": "individual",
  "correlationId": "98d4dac61c977c1b3f81d6ab78d29c3c",
  "sandboxMode": false,
  "externalUserId": "customExternalUserId",
  "type": "applicantKytOnHold",
  "reviewStatus": "onHold",
  "createdAt": "2025-01-30 11:41:55+0000",
  "createdAtMs": "2025-01-30 11:41:55+0000",
  "clientId": "coolClientId",
  "kytTxnId": "64a7dc05fbf57c624afcb72d",
  "kytDataTxnId": "b4xdq4qjh5qpo06r8cpunc",
  "kytTxnType": "travelRule"
}

applicantKytTxnRejected

If you receive applicantKytTxnRejected, do not submit the withdrawal until the issue is resolved or the transaction is intentionally declined.

{
  "applicantId": "634829375766b80001a40152",
  "applicantType": "individual",
  "correlationId": "0f5a7c828bab750775564534fc0470a8",
  "sandboxMode": false,
  "externalUserId": "customExternalUserId",
  "type": "applicantKytTxnRejected",
  "reviewResult": {
    "reviewAnswer": "RED",
    "reviewRejectType": "FINAL"
  },
  "reviewStatus": "completed",
  "createdAt": "2025-01-30 11:41:55+0000",
  "createdAtMs": "2025-01-30 11:41:55+0000",
  "clientId": "coolClientId",
  "kytTxnId": "64a7dc05fbf57c624afcb72d",
  "kytDataTxnId": "b4xdq4qjh5qpo06r8cpunc",
  "kytTxnType": "travelRule"
}

Step 6: Update the transaction with the blockchain ID

After the withdrawal is confirmed on-chain, update the Travel Rule data exchange transaction with the blockchain transaction ID via this API method.

An example of a request to update the data exchange transaction:

curl -X PATCH \
     'https://api.sumsub.com/resources/kyt/txns/66cd891eefa135789ce5264f/data/info' \
     -H 'content-type: application/json' \
     -d '{ "paymentTxnId": "3213654zdrgsetrr51435ergh453t5z43rb" }'

This links the Travel Rule record to the actual blockchain transfer and moves the transaction from completed to finished.

If the transaction is approved but you decide not to broadcast it on-chain, send a cancel request via this API method and treat the Travel Rule transaction as cancelled.

Request example:

curl -X POST \
     'https://api.sumsub.com/resources/api/tr/66cd891eefa135789ce5265g/cancel' \
     -H 'Content-Type: application/json'

Protocol requirements

Ensure that the data provided in the data exchange transaction aligns with these requirements to facilitate smooth integration with the CODE and GTR protocols.

Individual

Fields described in the following table are required for Individuals for both the CODE and GTR protocols.

Required field Description
externalUserId Unique user identifier.
type Must be individual.
paymentMethod

Contains accountId (e.g., wallet address).

  • Required for the applicant and the counterparty in CODE.
  • Required for the counterparty in GTR.
firstNameEn English-transliterated first name or full name is required.
lastNameEn English-transliterated last name or full name is required.
nameType Must be birthName.
dob

Date of birth.

  • Required for the applicant in CODE.
  • Required for the applicant and the counterparty in GTR.

Company

Fields described in the following table are required for Companies for both CODE and GTR protocols.

Required field Description
externalUserId Unique user identifier.
type Must be company.
paymentMethod

Contains accountId (e.g., wallet address).

  • Required for the applicant and the counterparty in CODE.
  • Required for the counterparty in GTR.
fullName Full legal name of the company.
address.country Country code (e.g., DEU for Germany).

Fields described in the following table are also required, but only for the CODE protocol.

Required field Description
ceo.firstNameEn English-transliterated first name of the CEO is required.
ceo.lastNameEn English-transliterated first name of the CEO is required.
ceo.nameType Must be birthName.