Skip to main content
This guide walks you through handling a Travel Rule requirement when creating a crypto withdrawal — from detecting the requirement on a quote to submitting the collected data with the transaction.

Prerequisites

Walkthrough

Detect the requirement

When a quote is returned, check the requirements array. If it contains travel-rule, the requirement must be resolved before the transaction can be created. If requirements is empty, proceed directly to creating the transaction.

Resolve the requirement

The Travel Rule Widget allows the user to resolve a travel rule requirement for a specific quote.

Create a widget session

Create a session tied to the quote by calling Create session with flow: withdrawal-form and the data property containing the quoteId. Each session is single-use and bound to a specific quote.
A successful response returns the session data needed to initialize the widget.
Widget sessions expire after 2 minutes. If the session expires before the user opens the widget — or while they are mid-form — the widget emits an error event. Create a new session and re-mount to let the user retry. If the quote has also expired, create a new quote first before creating a new session.

Set up the widget

Initialize the widget using the session returned from the API and mount it into your application. The widget does not unmount itself — always call unmount() after handling any event.
Both examples above are for web applications — either creating the iframe yourself or letting the SDK do it. For native apps using a WebView, see Native apps with the SDK for the SDK’s native-bundling pattern, or Setup with JavaScript for the no-SDK approach that loads the session url directly as the WebView’s top-level page.

Handle complete event

Once the user finishes the form, the widget automatically resolves the RFI by calling Update request for information internally with the collected data.
Calling Update request for information yourself is only needed if you’re not using the widget and are resolving the Travel Rule requirement directly — not part of this widget-based flow.
When complete fires, unmount the widget and confirm the RFI is resolved before proceeding — whether it is immediately ok or still pending depends on the proof type used:
  • Synchronous proofs (self-declaration, cryptographic signature) — the RFI is fully resolved as soon as complete fires. If the quote has expired, create a new quote and proceed directly to Create transaction.
  • Asynchronous proofs (micro-transfer, e.g. a small on-chain test transaction) — complete firing does not mean the RFI is resolved yet; resolution depends on the micro-transfer settling on-chain and confirmed by Uphold. Wait for it to settle, then create a new quote before creating the transaction.
Use Get request for information to check which proof type was used and confirm the RFI’s status.
The original quote may have expired while the user was completing the widget form. If so, create a new quote before proceeding — the Travel Rule data collected by the widget remains valid. Include the same travelRule object in the transaction request with the updated quoteId.

Handle cancellations

The cancel event fires when the user closes the widget without completing the form. The quote is not affected — it remains valid until it expires, so a new widget session can be created for the same quote to let the user retry. See cancel event for the event reference.

Handle errors

The error event fires when an unrecoverable error occurs. See error event for the full error shape and available properties.

Transaction failures

Unlike a deposit hold, a withdrawal transaction is created right away, so failures surface after creation — either when the Travel Rule payload is rejected at creation time, or when the counterparty VASP later rejects the data.

Transaction creation errors

A transaction with an unspecified-error can indicate a rejected Travel Rule payload. The transaction was not completed. Collect fresh data via the widget and retry — the original quoteId remains valid unless it has since expired, in which case create a new quote first.

Counterparty rejection

After the transaction is created, the counterparty VASP has a window to review the Travel Rule data. If the data is rejected, Uphold emits a core.transaction.status-changed webhook with status: failed and statusDetails.reason: travel-rule-verification-failed. Common causes are the transaction being created before the RFI was fully resolved, or the beneficiary VASP being unrecognized or invalid. Notify the user and ask them to retry with a different destination.

Testing

To trigger a Travel Rule requirement on a withdrawal, use a GB user account and create an XRP withdrawal to an external address for 30 XRP. Verify the following:
  1. The quote response includes "travel-rule" in the requirements array.
  2. After creating a widget session, complete the form — verify via Get request for information that the RFI status is ok.
  3. Create a new quote with the same parameters and call Create transaction with the new quoteId; the transaction is created successfully.
  4. A core.transaction.status-changed webhook is received with status: completed (or failed if the counterparty rejects the data).
  5. The transaction status updates to completed within a few minutes, assuming no other blockers.
For the deposit flow, see Travel Rule — deposit flow.