> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getsinbad.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getsinbad.com/_mcp/server.

# Sell walkthrough

> Walk a customer through selling USDT to a merchant, from the first offer to the release.

On a SELL, the customer's own USDT is locked in escrow and the merchant pays fiat to the customer's account outside Sinbad. The customer's wallet therefore needs enough USDT, enough POL for network fees, and a sufficient token allowance before the escrow transaction. This walkthrough assumes an ordinary customer wallet. See [Wallets and gas](/wallets-and-gas) for signing and fees.

## Before you start

* A customer session for the customer. See [Create a session](/sessions).
* A saved payment account where the customer will receive fiat. See [Saved payment accounts](/payment-accounts).
* A customer wallet with enough USDT for the sale and enough POL for network fees on up to four transactions: two approvals, the escrow creation and the release.

## 1. Ask for an offer

Call [`POST /v1/offers/best`](https://docs.getsinbad.com/api-reference/offers-and-breakdowns/find-best-offer) with `side` set to `SELL`. The request is the same as in the [Buy walkthrough](/buy-walkthrough), with these differences:

* With `amount.mode` set to `FIAT`, `value` is the market value of the USDT in the selected currency, before merchant margin and fees. Sinbad converts it to USDT at the market rate, then deducts margin and fees from the fiat proceeds. The offer returns the resulting USDT quantity in `breakdownInput.amount`.
* With `USDT`, `value` is an exact token quantity.
* Always send `savedPaymentMethodId`, the account that receives the fiat. If you omit it, Sinbad uses the customer's first saved account for that payment method.

An amount outside every listing's limits returns `200` with empty results. If the only match is a listing that uses the customer's own wallet, the call fails with `400 VALIDATION_ERROR` and `details.reason: "SELF_TRADE_NOT_ALLOWED"`. Offer and breakdown requests share a rate limit per customer. See [Limits and limitations](/limits-and-limitations#rate-limits).

## 2. Create the breakdown

Call [`POST /v1/trade-breakdowns`](https://docs.getsinbad.com/api-reference/offers-and-breakdowns/create-trade-breakdown) with the chosen offer's `breakdownInput`, exactly as received, and an `Idempotency-Key` header. Send every field unchanged, including `paymentMethodId` and `savedPaymentMethodId`. A changed selection can return `400` or `409`, and a missing selection can return `404`. In either case, ask for a fresh offer.

Sinbad responds with `201`:

* `breakdownId` and `status: "AWAITING_FUNDING"`.
* `expiresAt`, 300 seconds (5 minutes) after creation.
* `tradeDetails`, including `YouReceive`.
* `escrowTransaction`, the prepared transaction for the customer's wallet to sign.
* `approvalRequired`, with an `approval` snapshot of the wallet's current allowance.

Show `tradeDetails` to the customer and continue only if they agree.

If the wallet holds less USDT than the amount to sell, the call fails with `400 INSUFFICIENT_BALANCE`. It does not include `details.kind: "NATIVE_GAS"`, because this is a token shortage, not a fee shortage. The failed call can still hold the customer's open SELL trade. After the customer tops up, repeat the identical request with the same `Idempotency-Key` while the breakdown is live, or use a new key to get a fresh quote that replaces it.

> **Note**
>
> See [One open trade per side](/offers-quotes#one-open-trade-per-side) for when a new breakdown replaces the earlier one and when it fails.

## 3. Approve the token if needed

If `approvalRequired` is `true`, call [`POST /v1/trade-breakdowns/{breakdownId}/approval`](https://docs.getsinbad.com/api-reference/transactions/prepare-token-approval) with an `Idempotency-Key` header and an empty body. Sinbad returns up to two prepared transactions, in the order to run them:

* If the wallet already has a non-zero allowance that is too small, the first is an `approvalReset` (approve `0`) and the second is the `approval` for the exact amount.
* Otherwise, there is one `approval`.
* If no approval is needed, `transactions` is empty.

Sign and submit each one, and wait until it is `CONFIRMED` before you sign the next. Sinbad checks the escrow transaction against the current chain state, so it fails until the allowance is confirmed. Approval does not create a trade and does not extend the 300-second deadline.

If an approval fails and you need a new one, call the endpoint again with a new `Idempotency-Key`. The same key returns the same prepared transactions.

## 4. Sign and submit the escrow transaction

Sign `escrowTransaction` and call [`POST /v1/transactions`](https://docs.getsinbad.com/api-reference/transactions/submit-prepared-transaction) with its `preparationId` and an `Idempotency-Key`. This locks the customer's USDT.

```http
POST /v1/transactions
Idempotency-Key: tx-<preparationId>
Content-Type: application/json

{ "preparationId": "<escrowTransaction.preparationId>", "signedTransaction": "0x02..." }
```

> **Warning**
>
> Confirm every required approval before you submit the escrow transaction, and make sure the escrow transaction is included in a block before `expiresAt`. Confirmation tracking can finish after `expiresAt`, so keep polling the existing transaction.

The wallet needs POL for every transaction. If it is short, the call fails with `400 INSUFFICIENT_BALANCE` and `details.kind: "NATIVE_GAS"`. For nonces, retries and unknown outcomes, see [Wallets and gas](/wallets-and-gas).

## 5. Wait for the trade

The trade exists only after the escrow is confirmed on chain. Either:

* Poll [`GET /v1/transactions/{preparationId}`](https://docs.getsinbad.com/api-reference/transactions/get-prepared-transaction-status) until `transaction.status` is `CONFIRMED`, or
* Poll [`GET /v1/trade-breakdowns/{breakdownId}`](https://docs.getsinbad.com/api-reference/offers-and-breakdowns/get-trade-breakdown) until `status` is `CONFIRMED` and `tradeId` is set.

The breakdown's other statuses are `AWAITING_FUNDING`, `EXPIRED` and `REVIEW_REQUIRED`. You can also use the [`trade.created`](/webhook-events/trade-created) and [`trade.updated`](/webhook-events/trade-updated) webhooks. The new trade starts in `CREATED`. Trade status can lag the chain, so keep polling until confirmed and reconcile with [`GET /v1/trades/{tradeId}`](https://docs.getsinbad.com/api-reference/trades/get-partner-trade).

On a funded SELL trade in `CREATED`, `PAID` or `DISPUTED`, `paymentInstructions` contains the customer's saved receiving-account snapshot. It becomes `null` after settlement. Trade lists and webhooks show only a `displayLabel`, never the account data.

## 6. The merchant pays the fiat

The merchant sends fiat to the customer's saved account and marks the trade paid. The trade becomes `PAID`. The merchant may upload proof of payment, which the customer can read with [`GET /v1/trades/{tradeId}/proof`](https://docs.getsinbad.com/api-reference/payment-proof/get-partner-trade-proof). The customer cannot upload proof on a SELL. See [Payment proof](/payment-proof).

## 7. The customer releases

Release the USDT only after the fiat has actually arrived:

1. Call [`POST /v1/trades/{tradeId}/actions/{action}/execution`](https://docs.getsinbad.com/api-reference/trade-actions/prepare-partner-customer-action) with the `release` action. The call takes no body and no `Idempotency-Key`, and every call returns a new prepared transaction.
2. Have the wallet sign it.
3. Submit it with [`POST /v1/transactions`](https://docs.getsinbad.com/api-reference/transactions/submit-prepared-transaction), which does need an `Idempotency-Key`.

```http
POST /v1/trades/{tradeId}/actions/release/execution
-> { "transaction": { "preparationId": "...", "purpose": "release", ... } }

POST /v1/transactions
Idempotency-Key: tx-<preparationId>

{ "preparationId": "<transaction.preparationId>", "signedTransaction": "0x02..." }
```

The USDT goes to the merchant and the trade becomes `RELEASED`. The customer's SELL side is free for a new trade. See [`release`](/trade-actions/release) and [Trade actions](/trade-actions).

## What can go wrong

| Situation                                  | What to do                                                                                                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| The fiat does not arrive                   | A SELL has no cancel action. While the trade is `PAID`, the customer can open a dispute at any time with the reason `payment_not_received`. While it is still `CREATED`, a dispute is possible only after the dispute window has passed. Before that, preparing the dispute fails with `409 CONFLICT`, `details.reason: "TOO_EARLY"` and `details.availableAt`. See [Disputes](/disputes). |
| The trade stays open                       | Sinbad does not automatically cancel an unpaid SELL as it does for a BUY. Open a dispute when it is allowed, or contact Sinbad support.                                                                                                                                                                                                                                                    |
| The wallet is short of USDT                | See step 2.                                                                                                                                                                                                                                                                                                                                                                                |
| The customer needs to talk to the merchant | Use [Chat and images](/trade-chat). It works throughout the trade.                                                                                                                                                                                                                                                                                                                         |

## Next steps

* [Disputes](/disputes) for the timing and resolution rules.
* [Trades and history](/trade-history) to list a customer's trades.
* [Go-live checklist](/go-live) before you send real customers to production.