How it works
Travel Rule outcomes in sandbox are driven by two layers:- Wallet screening (synchronous, at create time): resolves a destination to one of four categories — VASP-attributed, self-hosted, elevated risk, or sanctions match. Magic-suffix encoded.
- Counterparty webhook (asynchronous): the VASP counterparty’s terminal decision —
acknowledged,approved,rejected,declined. The sandbox automatically delivers the suffix-encoded outcome 10 seconds after the Travel Rule transfer is created, unless you call the simulate endpoint first to override it.
Wallet screening scenario catalog
Counterparty-outcome scenario catalog (VASP-attributed only)
These suffixes route through the Travel Rule flow. Unless you call the simulate endpoint first, the sandbox automatically delivers the suffix-encoded counterparty outcome 10 seconds after the Travel Rule transfer is created. Callingpayouts/:id/simulate/counterparty-webhook before that 10-second window elapses overrides the automatic outcome. A call that arrives after the automatic outcome has already been recorded does not change it.
The Travel Rule transfer is created after compliance and document review clear, just before the customer is asked to sign, so it exists throughout the signing window up to broadcast. A
rejected / declined outcome received before signing begins terminates the payout immediately, before broadcast. One received while the payout is still collecting signatures is held; the sandbox checks it again once the signatures reach quorum, and if the rejection is still on record it terminates the payout there, before broadcast. Once the payout has moved past that point toward broadcast, a rejection has no effect.
Pre- vs post-broadcast
Pre-broadcast: counterparty REJECTED / DECLINED cancels and terminalizes the transaction. Post-broadcast: audit-only; the payout completes. A confirmed chain transfer cannot be unwound (FATF Rec. 16).How to guarantee a pre-broadcast terminal rejection
A counterparty rejection arrives pre-broadcast as long as the payout has not yet broadcast on-chain. Travel Rule runs before signing, so the Travel Rule transfer exists throughout the signing window and right up to broadcast — a reject stays pre-broadcast until the payout broadcasts, after which it becomes audit-only. Two reliable approaches:- Manual counterparty call: call
payouts/:id/simulate/counterparty-webhook { outcome: "rejected", reason: "..." }once the Travel Rule transfer exists — before the customer signs, or once the signatures reach quorum. A call made while the payout is still collecting signatures is held; the sandbox checks it again when quorum is reached. In both cases the transaction terminates withfailureCode: travel_rule_rejected, as long as it has not already broadcast. - Unconditional pre-broadcast suffix: use destination suffix
BAD7E517. This is unconditionally pre-broadcast; no manual call is needed.
GET /v2/transactions/:id and the transaction.failed webhook carry the same failureMessage: the standard text for travel_rule_rejected, as in production. The optional reason does not appear in it. See the failureMessage symmetry contract.
Override the auto-pilot
To inject a counterparty webhook synchronously (before the 10s timer), call:rejected / declined: see the pre- vs post-broadcast carve-out above — post-broadcast, the call is audit-only and the payout completes. For acknowledged / approved: always informational; the payout proceeds independently of counterparty resolution. A later call that would regress an already-recorded outcome is ignored.
reason stays internal. The reason you supply is recorded for Conduit operators. The polled GET /v2/transactions/:id and the transaction.failed webhook carry the standard failureMessage for travel_rule_rejected, as in production. Correlate the simulator call with the resulting webhook by transactionId.Example: VASP destination + REJECTED counterparty
amount is a canonical decimal string with exactly asset.precision digits after . (USDC has 6 decimals → "10.000000" = 10 USDC).
Lifecycle:
- Wallet screening resolves VASP-attributed; the synthetic Travel Rule transfer row is written and the auto-pilot job is queued — all before the customer is asked to sign.
- The payout reports
requiresUserSignature: trueand waits while the customer’s authorized signers approve it. The Travel Rule row already exists, so a counterparty rejection can land during this window. - The synthetic counterparty webhook fires with
outcome: rejected. Broadcast has not yet happened; the payout terminates withtransaction.failedcarryingfailureCode: travel_rule_rejected. The funds reserved for the payout are returned; Conduit also notifies the counterparty VASP of the cancellation, on a best-effort basis.