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

# Buy via the REST API

> Let users buy crypto with a fiat payment method — card or bank — using the Uphold REST API.

export const feature_0 = "deposit"

A buy moves value into the user's Uphold crypto account, funded from a fiat external account (card or bank).

## Prerequisites

* The user has [completed onboarding](/developer-guides/user-onboarding/overview).
* **Capabilities** — card deposits require the `cards` capability and the fiat → crypto leg requires `trades` (UK retail users have a 24-hour post-onboarding cooldown before trading). Bank deposit capability varies by rail — see the per-rail guides.
* The integration uses Uphold's rails. Partner-owned rails (omnibus) follow a different flow not covered here.

## Walkthrough

<Tip>Prefer to click through the flow? See the [interactive buy walkthrough](/developer-guides/buy-and-sell/buy/interactive-buy-flow).</Tip>

***

## Select a crypto to buy

Call [List assets](/rest-apis/core-api/assets/list-assets) to retrieve the assets available on the platform. Filter to assets that include `buy` in their `features` array to show only purchasable assets.

```http theme={null}
GET /core/assets
```

```json [expandable] theme={null}
{
  "assets": [
    {
      "code": "BTC",
      "name": "Bitcoin",
      "type": "crypto",
      "symbol": "₿",
      "decimals": 8,
      "features": [
        "buy",
        "deposit",
        "sell",
        "transfer",
        "withdraw"
      ]
    },
    {
      "code": "ETH",
      "name": "Ethereum",
      "type": "crypto",
      "symbol": "Ξ",
      "decimals": 18,
      "features": [
        "buy",
        "deposit",
        "sell",
        "transfer",
        "withdraw"
      ]
    }
  ]
}
```

The chosen asset (`BTC` in this example) becomes the `destination.asset` for the buy.

Call [List accounts](/rest-apis/core-api/accounts/list-accounts) to find the user's existing Uphold accounts for that asset.

```http theme={null}
GET /core/accounts
```

If the user does not yet have an account for the chosen asset, create one with [Create account](/rest-apis/core-api/accounts/create-account).

```http theme={null}
POST /core/accounts
{
  "label": "My BTC Account",
  "asset": "BTC"
}
```

## Define the source

The source determines how the buy is funded. Choose the path that matches the user's intent:

### Fiat external account

The user funds the buy from a linked card or bank account.

#### Check available rails

Call [List Rails](/rest-apis/core-api/assets/list-rails) to verify the deposit rail you plan to use is available.

**Card:**

```http theme={null}
GET /core/rails?type=card
```

```json theme={null}
{
  "rails": [
    {
      "type": "card",
      "network": "visa",
      "method": "debit-card",
      "asset": "USD",
      "decimals": 2,
      "features": [
        "deposit",
        "withdraw"
      ]
    }
  ]
}
```

**Bank (example: SEPA):**

```http theme={null}
GET /core/rails?type=bank&network=sepa&asset=EUR
```

```json theme={null}
{
  "rails": [
    {
      "type": "bank",
      "network": "sepa",
      "method": "bank-transfer",
      "asset": "EUR",
      "decimals": 2,
      "features": [
        "deposit",
        "withdraw"
      ]
    }
  ]
}
```

#### Link or select the external account

Let the user link a new external account or select an existing one.

**Card:**

Call [List external accounts](/rest-apis/core-api/external-accounts/list-external-accounts) to fetch the user's saved cards.

```http theme={null}
GET /core/external-accounts
```

Make sure the selected card has `status: "ok"` and **{feature_0}** in `features`.

```json theme={null}
{
  "externalAccounts": [
    {
      "id": "7d5928c5-8ac4-4b0d-8b45-f332ba6a9de7",
      "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
      "type": "card",
      "status": "ok",
      "label": "My Visa Card",
      "asset": "GBP",
      "network": "visa",
      "features": [
        "deposit",
        "withdraw"
      ],
      "details": {
        "type": "debit",
        "last4Digits": "5119",
        "expiryDate": {
          "month": 12,
          "year": 2028
        },
        "octSupport": "supported"
      }
    }
  ]
}
```

If the user wants to use a card not yet on file, call [Create external account](/rest-apis/core-api/external-accounts/create-external-account) with the card details.

```http theme={null}
POST /core/external-accounts
{
  "type": "card",
  "label": "My Visa Card",
  "number": "4921817844445119",
  "securityCode": "123",
  "expiryDate": {
    "month": 12,
    "year": 2028
  }
}
```

The response initially returns `status: "processing"` while the card is validated.

```json theme={null}
{
  "externalAccount": {
    "id": "7d5928c5-8ac4-4b0d-8b45-f332ba6a9de7",
    "type": "card",
    "status": "processing",
    "label": "My Visa Card"
  }
}
```

Once validated, the status transitions to `ok` and the full details are available.

```json theme={null}
{
  "externalAccount": {
    "id": "7d5928c5-8ac4-4b0d-8b45-f332ba6a9de7",
    "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
    "type": "card",
    "status": "ok",
    "label": "My Visa Card",
    "asset": "GBP",
    "network": "visa",
    "features": [
      "deposit",
      "withdraw"
    ],
    "details": {
      "type": "debit",
      "last4Digits": "5119",
      "expiryDate": {
        "month": 12,
        "year": 2028
      },
      "octSupport": "supported"
    }
  }
}
```

<Note>Monitor the external account status via [Get external account](/rest-apis/core-api/external-accounts/get-external-account) or the `external-account.status-changed` webhook until `status` is `ok` before proceeding.</Note>

### EMD disclaimer

This disclaimer is required to comply with FCA regulations. Display it to users based in GB, the first time they link a credit/debit card or generate bank deposit details — it only needs to be shown once:

> Uphold Europe Limited is an EMD Agent of Optimus Cards UK Limited (FRN: 902034). All received funds are held in a designated safekeeping account with a regulated bank and kept separate from Uphold's own assets. These funds are not protected by the UK Financial Services Compensation Scheme.

**Bank:**

For bank accounts, the linking flow varies by rail. See the per-rail guides:

* [SEPA](/developer-guides/bank-transfers/deposit/via-rest-api/sepa)
* [ACH](/developer-guides/bank-transfers/deposit/via-rest-api/ach)
* [FPS](/developer-guides/bank-transfers/deposit/via-rest-api/fps)
* [FedNow](/developer-guides/bank-transfers/deposit/via-rest-api/fednow)
* [Wire](/developer-guides/bank-transfers/deposit/via-rest-api/wire)

Once the user has a linked bank external account, retrieve it with [List external accounts](/rest-apis/core-api/external-accounts/list-external-accounts).

```http theme={null}
GET /core/external-accounts
```

```json [expandable] theme={null}
{
  "externalAccounts": [
    {
      "id": "c4a2e8b1-5d6f-4a3b-9e7c-8f1d2a3b4c5d",
      "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604",
      "type": "bank",
      "status": "ok",
      "label": "My SEPA Bank Account",
      "asset": "EUR",
      "network": "sepa",
      "features": [
          "withdraw"
      ],
      "details": {
          "bic": "EXAAAT2K",
          "iban": "AT48444441229809844"
      },
      "createdAt": "2025-03-10T09:03:45.321Z",
      "updatedAt": "2025-03-10T09:03:45.321Z"
    }
  ]
}
```

## Transaction preview

Show the user a preview of the buy before committing. The preview is a quote — a price-locked offer valid until `expiresAt`. The amount the user specifies is `denomination.amount`; set `denomination.asset` to indicate whether the amount is expressed in the source or destination asset.

<Info>Quotes expire quickly. Capture user confirmation before `expiresAt` and re-quote if the user hesitates.</Info>

### Fiat external account

A fiat buy runs in two sequenced legs: a fiat deposit into the user's Uphold fiat account, followed by a fiat-to-crypto trade. Create a quote for each leg.

**Deposit quote — card:**

```http theme={null}
POST /core/transactions/quote
{
  "origin": {
    "type": "external-account",
    "id": "aa6e6efa-8d73-497c-8278-0347f459bd68"
  },
  "destination": {
    "type": "account",
    "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8"
  },
  "denomination": {
    "asset": "USD",
    "amount": "300.00",
    "target": "origin"
  }
}
```

```json [expandable] theme={null}
{
  "quote": {
    "id": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "aa6e6efa-8d73-497c-8278-0347f459bd68",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "expiresAt": "2026-06-01T12:00:30Z"
  }
}
```

**Deposit quote — bank (example: SEPA):**

```http theme={null}
POST /core/transactions/quote
{
  "origin": {
    "type": "external-account",
    "id": "b7f3c2d9-4e1a-4b8f-9c2e-5d6a7b8c9d0e"
  },
  "destination": {
    "type": "account",
    "id": "c11608ff-739d-4f38-bf91-f2d51c3b9c19"
  },
  "denomination": {
    "asset": "EUR",
    "amount": "300.00",
    "target": "origin"
  }
}
```

```json [expandable] theme={null}
{
  "quote": {
    "id": "b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6",
    "origin": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "b7f3c2d9-4e1a-4b8f-9c2e-5d6a7b8c9d0e",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "c11608ff-739d-4f38-bf91-f2d51c3b9c19",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "EUR",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "expiresAt": "2026-06-01T12:00:30Z"
  }
}
```

**Trade quote (fiat → crypto):**

After presenting the deposit quote, also create the trade quote so the user can see the full buy preview — the deposit amount and the crypto amount they will receive — before confirming.

<Note>For **bank deposits**, this trade quote is for preview only. Bank deposits settle asynchronously (hours to days), so this quote will have expired long before the deposit lands. Re-quote at the time you confirm the trade leg, after the deposit `status: "completed"` webhook fires.</Note>

```http theme={null}
POST /core/transactions/quote
{
  "origin": {
    "type": "account",
    "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8"
  },
  "destination": {
    "type": "account",
    "id": "f8c2d1e4-3a7b-4c9d-8e5f-2b1a6d3c7e8f"
  },
  "denomination": {
    "asset": "USD",
    "amount": "300.00",
    "target": "origin"
  }
}
```

```json [expandable] theme={null}
{
  "quote": {
    "id": "c3d4e5f6-a7b8-4c9d-0e1f-a2b3c4d5e6f7",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "0.003",
      "asset": "BTC",
      "rate": "0.00001",
      "node": {
        "type": "account",
        "id": "f8c2d1e4-3a7b-4c9d-8e5f-2b1a6d3c7e8f",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "0.00001"
    },
    "fees": [],
    "expiresAt": "2026-06-01T12:01:30Z"
  }
}
```

## Confirm quote

Commit the buy by calling [Create transaction](/rest-apis/core-api/transactions/create-transaction) with the quote ID.

### Fiat external account

Confirm the deposit leg first, then the trade leg after the deposit settles.

**Deposit — card (requires 3DS):**

Always include the `authorize:3ds` requirement — the card issuer may require 3DS authorization. You may embed stateful data in the `returnUrl` query parameters to resume the flow after redirect.

Include `X-Uphold-User-Ip` and `X-Uphold-User-Agent` headers — they are used by the platform for 3DS risk evaluation.

```http theme={null}
POST /core/transactions
X-Uphold-User-Ip: <user-ip>
X-Uphold-User-Agent: <user-agent>
{
  "quoteId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
  "params": {
    "requirements": [
      {
        "name": "authorize:3ds",
        "returnUrl": "https://example.com/return"
      }
    ]
  }
}
```

```json [expandable] theme={null}
{
  "transaction": {
    "id": "d4e5f6a7-b8c9-4d0e-1f2a-b3c4d5e6f7a8",
    "status": "processing",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "aa6e6efa-8d73-497c-8278-0347f459bd68",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604",
        "confirmationUrl": "https://authentication-devices.sandbox.checkout.com/sessions-interceptor/sid_zrih63pnk6ietkpuerqiyd5zga"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:00:00Z",
    "createdAt": "2026-06-01T12:00:05Z",
    "updatedAt": "2026-06-01T12:00:05Z"
  }
}
```

If `origin.node.confirmationUrl` is present, redirect the user to that URL to complete the 3DS challenge. After completion, the issuer redirects the user back to your `returnUrl`. Wait for the `core.transaction.status-changed` webhook with `status: "completed"` before proceeding to the trade leg.

**Deposit — bank:**

Bank deposits do not require a `returnUrl` or 3DS.

```http theme={null}
POST /core/transactions
{
  "quoteId": "b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6"
}
```

```json [expandable] theme={null}
{
  "transaction": {
    "id": "e5f6a7b8-c9d0-4e1f-2a3b-c4d5e6f7a8b9",
    "status": "processing",
    "origin": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "b7f3c2d9-4e1a-4b8f-9c2e-5d6a7b8c9d0e",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "c11608ff-739d-4f38-bf91-f2d51c3b9c19",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "EUR",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:00:00Z",
    "createdAt": "2026-06-01T12:00:05Z",
    "updatedAt": "2026-06-01T12:00:05Z"
  }
}
```

<Note>Bank deposits settle asynchronously — instantly for FPS / FedNow, hours to days for ACH / SEPA / wire. See the per-rail bank-transfer deposit guides for timing details.</Note>

Wait for the `core.transaction.status-changed` webhook with `status: "completed"` before proceeding to the trade leg.

**Trade (fiat → crypto):**

Once the deposit settles, confirm the trade quote.

```http theme={null}
POST /core/transactions
{
  "quoteId": "c3d4e5f6-a7b8-4c9d-0e1f-a2b3c4d5e6f7"
}
```

```json [expandable] theme={null}
{
  "transaction": {
    "id": "f6a7b8c9-d0e1-4f2a-3b4c-d5e6f7a8b9c0",
    "status": "processing",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "0.003",
      "asset": "BTC",
      "rate": "0.00001",
      "node": {
        "type": "account",
        "id": "f8c2d1e4-3a7b-4c9d-8e5f-2b1a6d3c7e8f",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "0.00001"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:01:00Z",
    "createdAt": "2026-06-01T12:01:05Z",
    "updatedAt": "2026-06-01T12:01:05Z"
  }
}
```

Wait for the `core.transaction.status-changed` webhook with `status: "completed"` before notifying the user.

## Monitor for settlement

Monitor each transaction in the chain by listening for `core.transaction.status-changed` webhooks or polling. Each leg fires its own event — wait for `status: "completed"` on one leg before submitting the next.

### Via webhooks

Subscribe to `core.transaction.status-changed`. The event fires on every status transition; filter on `data.transaction.status` to detect terminal states:

* `completed` — transaction settled successfully
* `failed` — transaction failed; inspect `data.transaction.errors` for details

```json theme={null}
{
  "id": "f4a5b6c7-d8e9-4f0a-1b2c-d3e4f5a6b7c8",
  "type": "core.transaction.status-changed",
  "createdAt": "2026-06-01T12:01:00Z",
  "data": {
    "transaction": {
      "id": "d4e5f6a7-b8c9-4d0e-1f2a-b3c4d5e6f7a8",
      "status": "completed"
    }
  }
}
```

### Via polling

`GET /core/transactions/{transactionId}` returns the current state. Use polling as a fallback when webhook delivery cannot be guaranteed. Terminal statuses are `completed` and `failed`.

```http theme={null}
GET /core/transactions/a0b1c2d3-e4f5-4a6b-7c8d-e9f0a1b2c3d4
```

```json theme={null}
{
  "transaction": {
    "id": "a0b1c2d3-e4f5-4a6b-7c8d-e9f0a1b2c3d4",
    "status": "completed"
  }
}
```

<Tip>Prefer webhooks over polling in production. See [Webhooks](/rest-apis/webhooks) for setup instructions.</Tip>

## Notify the user

Display an in-app confirmation when each `core.transaction.status-changed` webhook fires with `status: "completed"`. Show a final confirmation once the last leg settles — the crypto is now credited to the user's Uphold account.

<Check>You now support crypto buy via the REST API.</Check>

## Sample transactions

A buy via a fiat external account produces two chained transactions — a fiat deposit and a fiat-to-crypto trade. Each follows the same lifecycle and fires its own `core.transaction.status-changed` webhook on completion.

### Card deposit

```json [expandable] theme={null}
{
  "transaction": {
    "id": "d4e5f6a7-b8c9-4d0e-1f2a-b3c4d5e6f7a8",
    "status": "completed",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "aa6e6efa-8d73-497c-8278-0347f459bd68",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:00:00Z",
    "createdAt": "2026-06-01T12:00:05Z",
    "updatedAt": "2026-06-01T12:01:00Z"
  }
}
```

### Bank deposit (SEPA)

```json [expandable] theme={null}
{
  "transaction": {
    "id": "e5f6a7b8-c9d0-4e1f-2a3b-c4d5e6f7a8b9",
    "status": "completed",
    "origin": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "external-account",
        "id": "b7f3c2d9-4e1a-4b8f-9c2e-5d6a7b8c9d0e",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "300.00",
      "asset": "EUR",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "c11608ff-739d-4f38-bf91-f2d51c3b9c19",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "EUR",
      "amount": "300.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:00:00Z",
    "createdAt": "2026-06-01T12:00:05Z",
    "updatedAt": "2026-06-01T12:01:00Z"
  }
}
```

### Fiat-to-crypto trade

```json [expandable] theme={null}
{
  "transaction": {
    "id": "f6a7b8c9-d0e1-4f2a-3b4c-d5e6f7a8b9c0",
    "status": "completed",
    "origin": {
      "amount": "300.00",
      "asset": "USD",
      "rate": "1",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "destination": {
      "amount": "0.003",
      "asset": "BTC",
      "rate": "0.00001",
      "node": {
        "type": "account",
        "id": "f8c2d1e4-3a7b-4c9d-8e5f-2b1a6d3c7e8f",
        "ownerId": "cd21b26d-35d2-408a-9201-b8fdbef7a604"
      }
    },
    "denomination": {
      "asset": "USD",
      "amount": "300.00",
      "target": "origin",
      "rate": "0.00001"
    },
    "fees": [],
    "quotedAt": "2026-06-01T12:01:00Z",
    "createdAt": "2026-06-01T12:01:05Z",
    "updatedAt": "2026-06-01T12:01:30Z"
  }
}
```

## Failure handling

| Leg            | Step        | Failure                                             | Funds                                                            |
| -------------- | ----------- | --------------------------------------------------- | ---------------------------------------------------------------- |
| Deposit (card) | Quote       | Card declined or 3DS failed (charge never captured) | Nothing moved; no fees charged                                   |
| Deposit (card) | Transaction | Quote expired before commit                         | No transaction created; create a new quote                       |
| Deposit (card) | Transaction | Card captured but later reversed by issuer          | Fiat balance reduced by reversal; Uphold absorbs processing fees |
| Deposit (card) | Transaction | `cards` capability not enabled                      | No transaction; prompt KYC                                       |
| Deposit (bank) | Transaction | Insufficient funds or mandate not authorised        | No deposit received; surface error to user                       |
| Deposit (bank) | Transaction | Settlement timeout (rail-dependent)                 | No deposit received; retry or contact support                    |

<Info>If the deposit leg completes but the fiat → crypto conversion fails, the user's fiat funds remain in their Uphold fiat account. Detect this via the `core.transaction.status-changed` webhook and either retry the conversion or surface the fiat balance to the user.</Info>

<Info>The `core.transaction.status-changed` webhook fires with `status: "failed"` for each failed transaction. </Info>
