Prerequisites
- The user has completed onboarding.
- The user has an internal Uphold crypto account with sufficient balance.
- Capabilities — card payouts require the
card-withdrawalscapability and a destination card withoctSupport: "supported"; the crypto → fiat leg of a bank sell requirestrades. Bank withdrawal 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
Select a crypto to sell
Call List accounts to retrieve the user’s Uphold accounts. Present crypto accounts with a non-zero balance as sellable.f8c2d1e4 / BTC in this example) is the origin for the sell.
Define the destination
The destination determines where the sell proceeds go. Choose the path that matches the user’s intent:Fiat external account
The user receives the proceeds in a linked card or bank account.Check available rails
Call List Rails to verify the payout rail you plan to use is available. Thewithdraw feature must be present for the rail to be usable as a sell destination.
Card:
Link or select the external account
Let the user link a new external account or select an existing one. Card: Call List external accounts to fetch the user’s saved cards.status: "ok" and in features.
status: "processing" while the card is validated.
ok and the full details are available.
Monitor the external account status via Get external account or the
external-account.status-changed webhook until status is ok before proceeding.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.
A freshly linked card returns
"octSupport": "unknown" while Uphold verifies OCT eligibility. Poll GET /core/external-accounts/{id} until details.octSupport is "supported" before using the card as a sell destination.type: "bank", status: "ok", and "withdraw" in features.
Transaction preview
Show the user a preview of the sell before committing. The preview is a quote — a price-locked offer valid untilexpiresAt. The amount the user specifies is denomination.amount; set denomination.asset to indicate whether the amount is expressed in the source or destination asset.
Quotes expire quickly. Capture user confirmation before
expiresAt and re-quote if the user hesitates.Fiat external account
Card (OCT): A card sell settles in a single transaction — the crypto account is debited, converted at the locked rate, and fiat is paid out to the card via OCT.Confirm quote
Commit the sell by calling Create transaction with the quote ID.Fiat external account
Card (OCT): NoreturnUrl or 3DS is needed — card sell transactions do not require user card authorization.
core.transaction.status-changed webhook with status: "completed" before notifying the user.
Bank — trade leg (crypto → fiat):
UK retail users may encounter a
409 user_capability_failure with restrictions: ["financial-promotion-cooldown-running"] for 24 hours after onboarding. See the trade guide for cooldown details.core.transaction.status-changed webhook with status: "completed" before proceeding to the withdrawal leg.
Bank — withdrawal leg (fiat → bank):
Once the trade settles and fiat lands in the user’s Uphold fiat account, confirm the withdrawal quote.
Bank withdrawals settle asynchronously — instantly for FPS / FedNow, hours to days for ACH / SEPA. See the per-rail bank-transfer withdrawal guides for timing details.
core.transaction.status-changed webhook with status: "completed" before notifying the user.
Monitor for settlement
Monitor each transaction in the chain by listening forcore.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 tocore.transaction.status-changed. The event fires on every status transition; filter on data.transaction.status to detect terminal states:
completed— transaction settled successfullyfailed— transaction failed; inspectdata.transaction.errorsfor details
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.
Notify the user
Display a confirmation when the finalcore.transaction.status-changed webhook fires with status: "completed". For card payouts, include the payout amount and currency so the user knows the card credit to expect. For bank withdrawals, confirm the amount and destination account once the withdrawal settles.
You now support crypto sell via the REST API.
Sample transaction
A sell to a card debits the user’s internal crypto account, converts at the locked quote rate, and pays out fiat to the linked card via OCT. The transaction fires a singlecore.transaction.status-changed webhook on completion.
Failure handling
Every terminal state —
completed or failed — fires a core.transaction.status-changed webhook. Listen for all three to keep your UI in sync.