Skip to main content
This guide covers installing the Travel Rule Widget SDK and setting it up in web and native applications.

Prerequisites

  • Access to Widgets API to create widget sessions.
  • A quote or transaction with the travel-rule requirement.

Installation

Install the SDK via npm:
npm install @uphold/enterprise-travel-rule-widget-web-sdk

Web app integration

Import and initialize the SDK in your application. Make sure the container that will host the widget has explicit dimensions in CSS. The Widget will fill the container bounds. Minimum recommended size is 400px × 600px.
import { TravelRuleWidget } from '@uphold/enterprise-travel-rule-widget-web-sdk';

// Create the widget instance
const widget = new TravelRuleWidget(session, { debug: true });

// Set up event handlers
widget.on('complete', (event) => {
  console.log('Travel Rule form completed:', event.detail.value);

  widget.unmount();
});

widget.on('cancel', () => {
  console.log('Travel Rule form cancelled by user');

  widget.unmount();
});

widget.on('error', (event) => {
  console.error('Travel Rule form error:', event.detail.error);

  widget.unmount();
});

// Mount the widget's iframe to a DOM element
widget.mountIframe(document.getElementById('tr-container'));
The session parameter is obtained from the Create Session endpoint.

Native app integration

Native mobile applications integrate the Travel Rule Widget using a WebView component that loads an HTML page containing the Travel Rule Widget SDK. To handle Travel Rule Widget events, your native application needs to implement a communication bridge between the WebView and native code. This bridge enables your native app to receive and respond to events from the Travel Rule Widget.

WebView HTML template

Create an HTML file that includes the Travel Rule Widget SDK in your JS bundle:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Travel Rule widget</title>
    <style>
      body { margin: 0; padding: 0; }
      #travel-rule-container { width: 100%; height: 100vh; }
    </style>
  </head>
  <body>
    <div id="travel-rule-container"></div>

    <!-- Include your JS bundle which contains the SDK -->
    <script src="your-bundle-with-sdk.js"></script>
    <script>
      // Initialize widget when page loads
      window.addEventListener('load', () => {
        const session = /* Obtain session from Create Session endpoint */;

        const widget = new TravelRuleWidget(session);

        widget.on('complete', (event) => {
          sendToNativeApp('complete', event.detail);
          widget.unmount();
        });

        widget.on('cancel', () => {
          sendToNativeApp('cancel');
          widget.unmount();
        });

        widget.on('error', (event) => {
          sendToNativeApp('error', event.detail.error);
          widget.unmount();
        });

        widget.mountIframe(document.getElementById('travel-rule-container'));
      });

      // Helper function to send events to native app
      function sendToNativeApp(type, data = null) {
        const message = { type, data };

        if (window.webkit?.messageHandlers?.travelRuleWidgetMessage) {
          // iOS - send all events through single handler
          window.webkit.messageHandlers.travelRuleWidgetMessage.postMessage(message);
        } else if (window.TravelRuleBridge) {
          // Android - send events through JavaScript interface
          window.TravelRuleBridge.onMessage(JSON.stringify(message));
        } else if (window.ReactNativeWebView) {
          // React Native - send events through postMessage
          window.ReactNativeWebView.postMessage(JSON.stringify(message));
        }
      }
    </script>
  </body>
</html>
The Travel Rule Widget SDK must be included in your WebView bundle (for example, via your build pipeline). Loading the SDK directly from a CDN is not supported.

Setting up the WebView

Configure the WebView and event bridge for your platform:
import WebKit

class TravelRuleViewController: UIViewController, WKScriptMessageHandler {
  @IBOutlet weak var webView: WKWebView!

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set up single message handler for all widget events
    let contentController = webView.configuration.userContentController
    contentController.add(self, name: "travelRuleWidgetMessage")

    // Load your HTML file with the widget
    if let url = Bundle.main.url(forResource: "travel-rule-widget", withExtension: "html") {
      webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
    }
  }

  // Handle messages from the WebView
  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "travelRuleWidgetMessage" {
      guard let messageDict = message.body as? [String: Any],
          let type = messageDict["type"] as? String else {
        return
      }

      let data = messageDict["data"]

      switch type {
        case "complete":
          handleTravelRuleComplete(data: data)
        case "cancel":
          handleTravelRuleCancel()
        case "error":
          handleTravelRuleError(error: data)
        default:
          print("Unknown Travel Rule widget message type: \(type)")
      }
    }
  }

  private func handleTravelRuleComplete(data: Any?) {
    // Handle successful Travel Rule compliance completion
    print("Travel Rule form completed: \(data ?? "no data")")
    // Proceed with the crypto transaction
  }

  private func handleTravelRuleCancel() {
    // Handle Travel Rule process cancellation
    print("Travel Rule form cancelled by user")
    // Navigate back or show cancellation message
  }

  private func handleTravelRuleError(error: Any?) {
    // Handle Travel Rule error
    print("Travel Rule form error: \(error ?? "unknown error")")
    // Show error message to user
  }
}

Troubleshooting

Widget not displaying The widget loads in an iframe, which requires proper Content Security Policy (CSP) configuration. Add the following domains to your CSP directives:
  • Sandbox: https://travel-rule-widget.enterprise.sandbox.uphold.com
  • Production: https://travel-rule-widget.enterprise.uphold.com
Example CSP configuration:
<meta 
  http-equiv="Content-Security-Policy" 
  content="frame-src 'self' https://travel-rule-widget.enterprise.sandbox.uphold.com https://travel-rule-widget.enterprise.uphold.com;"
>
Events not firing in native apps Verify that:
  • JavaScript is enabled in the WebView.
  • The message bridge is properly registered before the HTML page loads.
  • Event handlers match the platform-specific bridge implementation.

Next steps