> ## 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.

# Execute a trade via the REST API

> Convert between different assets for the same user using the Uphold quote → commit flow. Trades settle in under a second.

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](/developer-guides/user-onboarding/overview).
* 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](/rest-apis/core-api/capabilities/list-user-capabilities):

```http theme={null}
GET /core/users/{userId}/capabilities
```

```json theme={null}
{
  "code": "trades",
  "enabled": true,
  "restrictions": [],
  "requirements": []
}
```

## Walkthrough

<Tip>Prefer to click through the flow? See the [interactive trade walkthrough](/developer-guides/trade-and-send/trade/interactive-trade-flow) for a visual step-by-step guide.</Tip>

## 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](/rest-apis/core-api/accounts/list-accounts), or create the destination account with [Create account](/rest-apis/core-api/accounts/create-account).

```http theme={null}
GET /core/accounts?perPage=10
```

```json [expandable] theme={null}
{
  "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" }
    }
  ]
}
```

<Info>
  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.
</Info>

## 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.asset** | **denomination.target** | **Meaning**                                               |
| ---------------------- | ----------------------- | --------------------------------------------------------- |
| `origin.asset`         | `origin`                | Sell exactly the specified amount of the origin asset     |
| `destination.asset`    | `destination`           | Buy exactly the specified amount of the destination asset |

```http theme={null}
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"
  }
}
```

```json [expandable] theme={null}
{
  "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](/rest-apis/core-api/transactions/create-quote).

<Info>
  **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.
</Info>

## 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.

```http theme={null}
POST /core/transactions
{
  "quoteId": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab"
}
```

```json [expandable] theme={null}
{
  "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](/rest-apis/core-api/transactions/create-transaction).

## Confirm settlement

Trades typically reach a final state within a few hundred milliseconds.

#### Recommended: Webhooks

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.

```http theme={null}
GET /core/transactions/{transactionId}
```

```json theme={null}
{
  "transaction": {
    "id": "43c0c58d-6fbd-464a-bf2b-f38fbfebb4ab",
    "status": "completed"
  }
}
```

#### Trade transaction statuses

| Status       | Meaning                                                                                          |
| ------------ | ------------------------------------------------------------------------------------------------ |
| `processing` | Trade has been accepted and is being settled. This is the expected immediate state after commit. |
| `completed`  | Trade settled successfully and balances have been updated.                                       |
| `failed`     | Trade 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](/rest-apis/core-api/accounts/get-account).

<Check>
  You now support asset-to-asset trades using the Uphold Enterprise API Suite.
</Check>

## Common errors

| Status | Code                                                                                 | Cause                                                     | Recommended action                                                         |
| ------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------- | -------------------------------------------------------------------------- |
| `409`  | `user_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. |
| `409`  | `user_capability_failure` (`capability.code: "trades"`)                              | User does not have the `trades` capability enabled.       | Verify onboarding status and capability requirements.                      |
| `404`  | `entity_not_found` (`entity: "quote"`)                                               | Quote expired or no longer exists.                        | Request a new quote and ask the user to confirm again.                     |
| `409`  | `insufficient_balance`                                                               | Origin account does not have sufficient available funds.  | Refresh balances and prompt the user to enter a lower amount.              |
