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.Documentation Index
Fetch the complete documentation index at: https://developer.uphold.com/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
- The user has completed onboarding and has the required capabilities enabled.
Walkthrough
Detect the requirement
When a quote is returned, check therequirements 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 withflow: withdrawal-form and the quoteId. Each session is single-use and bound to a specific quote.
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 callunmount() after handling any event.
The example above is for web applications. For native apps using a WebView, see Native app integration.
Handle complete event
Once the widget emitscomplete, send the payload to your backend. The params.travelRule field is the complete event.detail.value object from the complete event, passed through unchanged — including its nested value field, which you do not unwrap. See complete event for the event reference. For withdrawals the data is nested under params.travelRule, unlike deposits where it goes in the top-level data field of the RFI update.
Handle cancellations
Thecancel 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
Theerror 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 anunspecified-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. This surfaces in two ways:- Transaction fails — Uphold emits a
core.transaction.status-changedwebhook withstatus: failedandstatusDetails.reason: travel-rule-verification-failed. Notify the user; they may need to retry with a different destination. - New RFI created — the counterparty requests additional information. An RFI of type
travel-ruleis attached to the transaction. List the pending RFIs viaGET /core/transactions/{id}/requests-for-information, create a widget session with therequestForInformationId, and resolve viaPUT /core/transactions/{id}/requests-for-information/{rfiId}. See the deposit guide for the full flow.
Testing
To trigger a Travel Rule requirement on a withdrawal, use a UK user account and create an XRP withdrawal to an external address for 30 XRP. Verify the following:- The quote response includes
"travel-rule"in therequirementsarray. - After creating a widget session, completing the widget flow, and calling Create transaction with the Travel Rule data, the transaction is created successfully.
- A
core.transaction.status-changedwebhook is received withstatus: completed(orfailedif the counterparty rejects the data). - The transaction status updates to
completedwithin a few minutes, assuming no other blockers.