Skip to content

Travel Rule

TL;DR

  • Travel rule = FATF regulation requiring crypto exchanges (VASPs) to collect and exchange sender/beneficiary information for transactions
  • Only applies to external blockchain transactions — internal transfers between Backpack users are completely excluded
  • Jurisdiction-based: EU → Sumsub, UAE → Sumsub, Japan → Sygna, everyone else → no travel rule
  • Ownership verification (Satoshi test) lets users prove they own an address, permanently bypassing travel rule for that address
  • Applies to both deposits and withdrawals, but with different flows

Scope: External Transactions Only

The travel rule never applies to internal transfers. This is enforced at multiple levels:

  • Deposits: DepositSource::Internal is explicitly removed from the external remitter's processing filter. Internal deposits skip the entire travel rule pipeline.
  • Withdrawals: Treasury withdrawal queries filter is_internal: false. Only external withdrawals enter the travel rule flow.

A withdrawal is classified as "internal" if:

  • Blockchain is Internal (direct account-to-account transfer)
  • Blockchain is EqualsMoney
  • The destination address belongs to another Backpack user (known deposit address in the system)

Jurisdiction Routing

When a deposit or withdrawal requires travel rule processing, the system routes to the appropriate verification provider based on the user's organization/jurisdiction:

JurisdictionOrganization IDVerification Provider
EUEUROPE_ORGANIZATION_ID = 2Sumsub
UAECountry code "AE"Sumsub
JapanJAPAN_ORGANIZATION_ID = 4Sygna
Everyone elseNo travel rule required

Key file: remitter/external/src/tasks/mod.rs — defines the TravelRuleVerification enum and jurisdiction routing logic.


Deposit Flow

StatusMeaning
SenderVerificationRequiredUser must fill out the sender questionnaire
SenderVerificationCompletedQuestionnaire done, routing to next step
OwnershipVerificationRequiredUser must complete a Satoshi test
SumsubTravelRulePendingWaiting for Sumsub verification result
SygnaTravelRulePendingWaiting for Sygna VASP-to-VASP verification
PendingTravel rule cleared, proceeding to credit

UAE Threshold Bypass

UAE users can bypass the full travel rule flow for deposits if:

  • The sender is the account owner themselves, AND
  • The cumulative 24-hour deposit value is below the ae_recipient_threshold

EU has a similar per-transaction eu_recipient_threshold.


Withdrawal Flow

StatusMeaning
TravelRuleNeeds travel rule evaluation
SumsubTravelRulePendingWaiting for Sumsub verification
VerifyingLimitsTravel rule cleared, checking withdrawal limits

Ownership Verification (Satoshi Test)

Ownership verification is a mechanism for users to cryptographically prove they control an external address. Once proven, that address permanently bypasses travel rule checks for both deposits and withdrawals.

How It Works

  1. User requests a Satoshi test: POST /wapi/v1/satoshi/ with { blockchain, address }
  2. System generates test parameters:
    • A random small amount (1-99 satoshis + blockchain-specific adjustments)
    • The user's deposit address for that blockchain
    • A 24-hour expiration window
  3. User sends the exact amount from the withdrawal address to their Backpack deposit address
  4. System detects the deposit, matches it to the pending test, and marks it complete
  5. Ownership record created in address_ownership table with the transaction hash as signature

Expiration

  • The Satoshi test has a 24-hour window to complete. If expired, user can create a new one.
  • The ownership record itself never expires. Once verified, the address is permanently verified for that user + blockchain combination.

What Happens on Completion

When a Satoshi test completes, the system atomically:

  1. Stores the ownership record (address_ownership table with information = 'SATOSHI_TEST_COMPLETED')
  2. Transitions any deposits from that address stuck in OwnershipVerificationRequiredPending
  3. Transitions any withdrawals to that address stuck in OwnershipVerificationRequiredVerifyingLimits

Database

sql
-- Ownership record (permanent)
CREATE TABLE address_ownership (
    id          SERIAL PRIMARY KEY,
    user_id     INTEGER NOT NULL REFERENCES users(id),
    address     TEXT NOT NULL,
    blockchain  BLOCKCHAIN NOT NULL,
    signature   TEXT,           -- transaction hash from completed test
    information TEXT,           -- 'SATOSHI_TEST_COMPLETED'
    created_at  TIMESTAMP,
    updated_at  TIMESTAMP,
    UNIQUE (user_id, address, blockchain)
);

-- Satoshi test (temporary, 24h expiry)
CREATE TABLE satoshi_test (
    id                 SERIAL PRIMARY KEY,
    user_id            INTEGER NOT NULL,
    blockchain         BLOCKCHAIN NOT NULL,
    address            TEXT NOT NULL,
    deposit_address_id INTEGER NOT NULL,
    quantity           NUMERIC(56, 28) NOT NULL,
    status             SATOSHI_TEST_STATUS NOT NULL DEFAULT 'PENDING',
    expires_at         TIMESTAMP NOT NULL,  -- created_at + 24 hours
    completed_at       TIMESTAMP,
    transaction_hash   TEXT
);

Verifying Limits

After travel rule clears, withdrawals enter the VerifyingLimits stage. This checks 4 daily limit categories (all in USD notional):

LimitScopeDefault Example
User symbol dailyPer-user, per-asset$100,000
Exchange symbol dailyExchange-wide, per-asset$1M-$5M
User total dailyPer-user, all assets combinedPer-user override
Exchange total dailyExchange-wide, all assets$20,000,000

Outcomes:

  • PassPending (withdrawal proceeds to signing/broadcast)
  • FailLimitsHold (manual review required, Slack notification sent)

Bypasses:

  • Internal transfers skip limit checks entirely
  • Market maker accounts skip limit checks entirely

Note: The API maps VerifyingLimits to "pending" for end users — they don't see this intermediate state.

Limit configuration tables: withdrawal_limit (per-asset defaults), withdrawal_limit_override (per-user per-asset), withdrawal_total_notional_limit_override (per-user total).


Sygna Integration (Japan)

Sygna is a VASP-to-VASP messaging network used for Japan travel rule compliance. It handles both incoming and outgoing transfers with cryptographic signing and encryption.

Webhook Endpoints

EndpointPurpose
POST /wapi/v1/sygna/webhook/v1/callbacks/permission-requestIncoming: another VASP asks to send funds to a Backpack user
POST /wapi/v1/sygna/webhook/v1/callbacks/permission-responseResponse: destination VASP approves/rejects our outgoing withdrawal
POST /wapi/v1/sygna/webhook/v1/callbacks/address-validationValidates if blockchain addresses belong to Backpack
GET /wapi/v1/sygna/webhook/v1/vasp-server-statusHealth check

Sumsub Integration (EU, UAE)

Sumsub handles travel rule verification for EU and UAE users. The flow differs slightly for individual vs entity accounts:

  • Individual users: Submitted to existing Sumsub applicant
  • Entity users: Submitted as non-existing applicant

Results stored in sumsub_withdrawal_travel_rule / sumsub_deposit_travel_rule tables with score, data (JSONB), review status.


Error Handling

Failures during travel rule processing result in:

  • Withdrawal/deposit status set to Review (manual review required)
  • Slack notification with failure reason
  • Processing removed from active queue

Key Files

FilePurpose
remitter/external/src/tasks/mod.rsTravelRuleVerification enum, jurisdiction routing
remitter/external/src/tasks/withdrawal/travel_rule.rsWithdrawal travel rule processing
remitter/external/src/tasks/withdrawal/limits.rsVerifying limits step
remitter/external/src/tasks/deposit/travel_rule/pending.rsInitial deposit travel rule checks
remitter/external/src/tasks/deposit/travel_rule/verify.rsPost-questionnaire verification routing
remitter/external/src/tasks/deposit/travel_rule/sygna.rsSygna deposit processing
api/src/routes/sygna.rsSygna webhook endpoints
api/src/routes/satoshi_test.rsSatoshi test API
store/src/satoshi_test.rsSatoshi test DB operations
store/src/address.rsWithdrawal address + ownership queries
store/src/kyt/deposit.rsDeposit travel rule DB operations
store/src/kyt/withdrawal.rsWithdrawal travel rule DB operations
api-admin/src/routes/travel_rule.rsAdmin APIs for viewing travel rule records