Skip to main content
A trade converts value between two accounts owned by the same user when those accounts hold different assets (for example, USD → BTC, ETH → USDC, or BTC → USD). Trades follow a simple two-step flow:
  1. Create a quote to lock the exchange rate and amounts.
  2. Commit the quote to execute the trade.
Once committed, the trade settles instantly and is typically completed in under a second.

Prerequisites

Before creating trades:
  • The user must have completed onboarding.
  • The user must have the trades capability enabled.
  • UK retail users must clear a mandatory 24-hour financial-promotion cooldown after onboarding before they can trade. Until it elapses, POST /core/transactions/quote returns 409 user_capability_failure with restrictions: ["financial-promotion-cooldown-running"] — surface this state explicitly in your UI rather than treating it as a generic failure.
Verify the capability using List capabilities:
GET /core/users/{userId}/capabilities
{
  "code": "trades",
  "enabled": true,
  "restrictions": [],
  "requirements": []
}

Walkthrough

Prefer to click through the flow? See the interactive trade walkthrough for a visual step-by-step guide.

Select the origin and destination accounts

A trade requires two accounts under the same ownerId:
  • Origin account — the asset being sold.
  • Destination account — the asset being purchased.
The two accounts must hold different assets. Retrieve existing accounts using List accounts, or create the destination account with Create account.
GET /core/accounts?perPage=10
{
  "accounts": [
    {
      "id": "11de2405-1f1f-4a6e-8a39-acd97dc7f3ec",
      "label": "My USD account",
      "asset": "USD",
      "balance": { "total": "250.00", "available": "250.00" }
    },
    {
      "id": "c392a0de-5fc6-49a7-96e1-e4b3f9ef5b27",
      "label": "My BTC account",
      "asset": "BTC",
      "balance": { "total": "0.0015", "available": "0.0015" }
    }
  ]
}
The origin account must have enough available balance to cover the trade.Only balance.available can be traded. Funds that are still pending settlement (balance.total > balance.available) cannot be used.

Create a quote

Create a quote to lock the exchange rate and determine the exact trade amounts. Specify the side you want to lock using denomination.target (origin or destination) and the amount using denomination.amount. Uphold calculates the corresponding amount on the opposite side. The quote response indicates which side was locked through denomination.target:
denomination.assetdenomination.targetMeaning
origin.assetoriginSell exactly the specified amount of the origin asset
destination.assetdestinationBuy exactly the specified amount of the destination asset
POST /core/transactions/quote
{
  "origin": {
    "type": "account",
    "id": "11de2405-1f1f-4a6e-8a39-acd97dc7f3ec"
  },
  "destination": {
    "type": "account",
    "id": "c392a0de-5fc6-49a7-96e1-e4b3f9ef5b27"
  },
  "denomination": {
    "asset": "USD",
    "amount": "100",
    "target": "origin"
  }
}
{
  "quote": {
    "id": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab",
    "expiresAt": "2026-05-29T11:47:05.109Z",
    "origin": { "asset": "USD", "amount": "100.00", "rate": "64564.596" },
    "destination": { "asset": "BTC", "amount": "0.00154883", "rate": "0.0000155" },
    "denomination": {
      "asset": "USD",
      "amount": "100.00",
      "rate": "1.00",
      "target": "origin"
    },
    "fees": []
  }
}
For request and response details, see Create a quote.
Honor the quote expiration.Each quote is valid only until the expiresAt timestamp in the response. Display the quote, collect the user’s confirmation, and commit before it expires.If the quote has expired, creating the transaction returns 404 entity_not_found. Create a new quote and ask the user to confirm again.

Commit the quote

After the user accepts the quoted rate, commit the quote by creating a transaction. The quoteId becomes the transactionId, allowing you to use a single identifier throughout the trade lifecycle.
POST /core/transactions
{
  "quoteId": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab"
}
{
  "transaction": {
    "id": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab",
    "status": "processing",
    "origin": { "asset": "USD", "amount": "100.00" },
    "destination": { "asset": "BTC", "amount": "0.00154883" }
  }
}
For implementation details, see Create a transaction.

Confirm settlement

Trades typically reach a final state within a few hundred milliseconds. Subscribe to: core.transaction.status-changed When the trade completes, you’ll receive a webhook containing the updated transaction status.

Alternative: Polling

Retrieve the transaction and poll until it reaches a terminal state.
GET /core/transactions/{transactionId}
{
  "transaction": {
    "id": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab",
    "status": "completed"
  }
}

Trade transaction statuses

StatusMeaning
processingTrade has been accepted and is being settled. This is the expected immediate state after commit.
completedTrade settled successfully and balances have been updated.
failedTrade could not be completed. Contact support and provide the transaction id.

Notify the user

Once settlement completes:
  • Display a trade confirmation.
  • Show both sides of the conversion.
  • Refresh account balances.
Example:
Sold 100.00 USD and received 0.00154883 BTC.
Refresh balances using Get account.
You now support asset-to-asset trades using the Uphold Enterprise API Suite.

Common errors

StatusCodeCauseRecommended action
409user_capability_failure (restrictions: ["financial-promotion-cooldown-running"])UK retail user is still in the mandatory cooldown period.Inform the user that trading becomes available after the cooldown expires.
409user_capability_failure (capability.code: "trades")User does not have the trades capability enabled.Verify onboarding status and capability requirements.
404entity_not_found (entity: "quote")Quote expired or no longer exists.Request a new quote and ask the user to confirm again.
409insufficient_balanceOrigin account does not have sufficient available funds.Refresh balances and prompt the user to enter a lower amount.