Skip to main content

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.

This guide walks you through supporting crypto deposits using the REST API.

Prerequisites

Some networks require destination tags/memos (e.g., XRP, XLM). If a reference is returned with the address, you can check its type by calling Get network endpoint. Educate the user on the importance of providing this information when making the deposit, as it is essential for crediting their account correctly.

Walkthrough

Check available rails

Call List rails to confirm the asset and network the user wants to deposit from, and verify the deposit feature is enabled.
GET /core/rails?type=crypto&asset=BTC
A successful response lists all rails for the asset. Each rail includes a features array — only proceed with networks that include "deposit". For assets supported by multiple networks, require an explicit network selection from the user.
{
  "rails": [
    {
      "type": "crypto",
      "network": "bitcoin",
      "method": "crypto-transaction",
      "asset": "BTC",
      "decimals": 8,
      "features": [
        "deposit",
        "withdraw"
      ]
    },
    {
      "type": "crypto",
      "network": "lightning",
      "method": "crypto-transaction",
      "asset": "BTC",
      "decimals": 8,
      "features": []
    }
  ]
}

Select destination account

Crypto deposits can target any account. If the selected account is not in the deposited asset, the amount will be converted at settlement using Uphold’s prevailing rate. Make sure the destination asset has the necessary features enabled.

Find an existing account

Call List accounts to retrieve the user’s accounts and let them pick the one they want to fund.
GET /core/accounts
{
  "accounts": [
    {
      "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
      "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
      "label": "My BTC account",
      "asset": "BTC",
      "balance": {
        "total": "0.05",
        "available": "0.05"
      }
    }
  ]
}

Create a new account

If the user has no accounts, create one with Create account before proceeding.
POST /core/accounts
{
  "label": "My BTC account",
  "asset": "BTC"
}
{
  "account": {
    "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
    "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
    "label": "My BTC account",
    "asset": "BTC",
    "balance": {
      "total": "0",
      "available": "0"
    }
  }
}

Generate deposit method

Call Set up account deposit method with the target account id, asset, and network.
PUT /core/accounts/{accountId}/deposit-method?type=crypto&asset=BTC&network=bitcoin
A successful response includes the address and, if applicable, a reference (e.g., destination tag, memo).
{
  "depositMethod": {
    "type": "crypto",
    "status": "ok",
    "details": {
      "network": "bitcoin",
      "asset": "BTC",
      "address": "tb1qgu0gacn9pqpnvlqclvdwyz4gfgxz8pptfz4emt"
    }
  }
}
The deposit method may initially return status: processing while the address is being prepared. Call Get account deposit method to confirm it is ready (status: ok) before displaying instructions to the user.
Render the address clearly and, if a reference is present, display it prominently and treat it as required input. We suggest also displaying a QR code to reduce input errors.

Monitor for the incoming transfer

Prefer webhooks for real-time updates, or fall back to polling if webhooks are not feasible.

Sample transaction

In a successful crypto deposit, the origin is represented as a crypto-address node reflecting the sender’s on-chain address. The destination is the account that was set up to receive the deposit.
{
  "transaction": {
    "id": "223c24c5-76c6-4553-91bc-5af519441f03",
    "origin": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "node": {
        "type": "crypto-address",
        "network": "bitcoin",
        "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
        "execution": {
          "mode": "onchain",
          "transactionHash": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
        }
      }
    },
    "destination": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
      }
    },
    "fees": [],
    "status": "completed",
    "quotedAt": "2024-07-24T15:02:39Z",
    "createdAt": "2024-07-24T15:22:39Z",
    "updatedAt": "2024-07-24T15:33:08Z",
    "denomination": {
      "asset": "BTC",
      "amount": "0.00121023",
      "rate": "1.00",
      "target": "origin"
    }
  }
}

Execution modes

Crypto deposits are processed using different execution modes depending on the environment and configuration:
  • On-chain execution: The transaction is processed directly on the blockchain network. The origin.node.execution.mode will be onchain and include a transactionHash as shown in the example above.
  • Off-chain execution: When both the sender and recipient are Uphold users, the transaction is processed internally within Uphold’s infrastructure. This eliminates network fees and is faster than on-chain processing. The origin.node.execution.mode will be offchain. The execution object for these transactions includes additional properties: accountOwnerId (the sender user ID) and accountId (the sender account ID).
  • Simulated execution: Used in development environments for testing purposes. The transaction appears as processed but does not affect actual blockchain state (user balances will be affected though). The origin.node.execution.mode will be simulated (Available soon).

Handle on-hold transactions

If the crypto deposit is placed on-hold with reason pending-requests-for-information, resolve the pending RFIs before the deposit can complete.

List pending RFIs

Call List requests for information to retrieve the pending RFIs for the transaction.
GET /core/transactions/{transactionId}/requests-for-information
{
  "requestsForInformation": [
    {
      "id": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8",
      "type": "travel-rule",
      "status": "pending",
      "data": {},
      "createdAt": "2024-07-24T15:22:39Z",
      "updatedAt": "2024-07-24T15:22:39Z"
    }
  ]
}

Travel Rule

Travel Rule is a regulatory requirement that mandates the collection and transmission of information about the originator and beneficiary of certain crypto transactions. Use the Travel Rule widget to collect the required information.

Create the widget session

Call Create session with "flow": "deposit-form" and the RFI id.
POST /widgets/travel-rule/sessions
{
  "flow": "deposit-form",
  "data": {
    "requestForInformationId": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8"
  }
}
{
  "session": {
    "flow": "deposit-form",
    "url": "https://widgets.uphold.com/travel-rule/sessions/xyz789",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "data": {
      "provider": "notabene",
      "parameters": {}
    }
  }
}

Open the widget

The example below is for web applications. For native apps using a WebView, see Native app integration.
import { TravelRuleWidget } from '@uphold/enterprise-travel-rule-widget-web-sdk';

const widget = new TravelRuleWidget<'deposit-form'>(session, { debug: true });

widget.on('complete', (event) => {
  const { value: travelRule } = event.detail;
  // Send travelRule data to your backend to update the RFI
  widget.unmount();
});
widget.on('cancel', error => ...);
widget.on('error', error => ...);

widget.mountIframe(document.getElementById('tr-deposit'));

Update the RFI

Once the widget emits complete, call Update request for information with the Travel Rule data.
PUT /core/transactions/{transactionId}/requests-for-information/{requestForInformationId}
{
  "data": {
    // Travel Rule data from complete event
  }
}
{
  "requestForInformation": {
    "id": "3f6d0c1e-a1bf-4b25-9802-2a3ee492d3c8",
    "type": "travel-rule",
    "status": "ok",
    "data": {},
    "createdAt": "2024-07-24T15:22:39Z",
    "updatedAt": "2024-07-24T15:25:12Z"
  }
}
Once the RFI is resolved, the transaction resumes processing automatically.

Notify the user

Display an in-app confirmation when the transaction is completed, and send an email if applicable.
You now support crypto deposits with the Enterprise API Suite.