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::Internalis 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:
| Jurisdiction | Organization ID | Verification Provider |
|---|---|---|
| EU | EUROPE_ORGANIZATION_ID = 2 | Sumsub |
| UAE | Country code "AE" | Sumsub |
| Japan | JAPAN_ORGANIZATION_ID = 4 | Sygna |
| Everyone else | — | No travel rule required |
Key file: remitter/external/src/tasks/mod.rs — defines the TravelRuleVerification enum and jurisdiction routing logic.
Deposit Flow
Deposit Statuses (Travel Rule Related)
| Status | Meaning |
|---|---|
SenderVerificationRequired | User must fill out the sender questionnaire |
SenderVerificationCompleted | Questionnaire done, routing to next step |
OwnershipVerificationRequired | User must complete a Satoshi test |
SumsubTravelRulePending | Waiting for Sumsub verification result |
SygnaTravelRulePending | Waiting for Sygna VASP-to-VASP verification |
Pending | Travel 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
Withdrawal Statuses (Travel Rule Related)
| Status | Meaning |
|---|---|
TravelRule | Needs travel rule evaluation |
SumsubTravelRulePending | Waiting for Sumsub verification |
VerifyingLimits | Travel 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
- User requests a Satoshi test:
POST /wapi/v1/satoshi/with{ blockchain, address } - 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
- User sends the exact amount from the withdrawal address to their Backpack deposit address
- System detects the deposit, matches it to the pending test, and marks it complete
- Ownership record created in
address_ownershiptable with the transaction hash assignature
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:
- Stores the ownership record (
address_ownershiptable withinformation = 'SATOSHI_TEST_COMPLETED') - Transitions any deposits from that address stuck in
OwnershipVerificationRequired→Pending - Transitions any withdrawals to that address stuck in
OwnershipVerificationRequired→VerifyingLimits
Database
-- 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):
| Limit | Scope | Default Example |
|---|---|---|
| User symbol daily | Per-user, per-asset | $100,000 |
| Exchange symbol daily | Exchange-wide, per-asset | $1M-$5M |
| User total daily | Per-user, all assets combined | Per-user override |
| Exchange total daily | Exchange-wide, all assets | $20,000,000 |
Outcomes:
- Pass →
Pending(withdrawal proceeds to signing/broadcast) - Fail →
LimitsHold(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
| Endpoint | Purpose |
|---|---|
POST /wapi/v1/sygna/webhook/v1/callbacks/permission-request | Incoming: another VASP asks to send funds to a Backpack user |
POST /wapi/v1/sygna/webhook/v1/callbacks/permission-response | Response: destination VASP approves/rejects our outgoing withdrawal |
POST /wapi/v1/sygna/webhook/v1/callbacks/address-validation | Validates if blockchain addresses belong to Backpack |
GET /wapi/v1/sygna/webhook/v1/vasp-server-status | Health 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
| File | Purpose |
|---|---|
remitter/external/src/tasks/mod.rs | TravelRuleVerification enum, jurisdiction routing |
remitter/external/src/tasks/withdrawal/travel_rule.rs | Withdrawal travel rule processing |
remitter/external/src/tasks/withdrawal/limits.rs | Verifying limits step |
remitter/external/src/tasks/deposit/travel_rule/pending.rs | Initial deposit travel rule checks |
remitter/external/src/tasks/deposit/travel_rule/verify.rs | Post-questionnaire verification routing |
remitter/external/src/tasks/deposit/travel_rule/sygna.rs | Sygna deposit processing |
api/src/routes/sygna.rs | Sygna webhook endpoints |
api/src/routes/satoshi_test.rs | Satoshi test API |
store/src/satoshi_test.rs | Satoshi test DB operations |
store/src/address.rs | Withdrawal address + ownership queries |
store/src/kyt/deposit.rs | Deposit travel rule DB operations |
store/src/kyt/withdrawal.rs | Withdrawal travel rule DB operations |
api-admin/src/routes/travel_rule.rs | Admin APIs for viewing travel rule records |