Sell walkthrough
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 for signing and fees.
Before you start
- A customer session for the customer. See Create a session.
- A saved payment account where the customer will receive fiat. See Saved 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 with side set to SELL. The request is the same as in the Buy walkthrough, with these differences:
- With
amount.modeset toFIAT,valueis 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 inbreakdownInput.amount. - With
USDT,valueis 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.
2. Create the breakdown
Call POST /v1/trade-breakdowns 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:
breakdownIdandstatus: "AWAITING_FUNDING".expiresAt, 300 seconds (5 minutes) after creation.tradeDetails, includingYouReceive.escrowTransaction, the prepared transaction for the customer’s wallet to sign.approvalRequired, with anapprovalsnapshot 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.
See 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 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(approve0) and the second is theapprovalfor the exact amount. - Otherwise, there is one
approval. - If no approval is needed,
transactionsis 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 with its preparationId and an Idempotency-Key. This locks the customer’s USDT.
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.
5. Wait for the trade
The trade exists only after the escrow is confirmed on chain. Either:
- Poll
GET /v1/transactions/{preparationId}untiltransaction.statusisCONFIRMED, or - Poll
GET /v1/trade-breakdowns/{breakdownId}untilstatusisCONFIRMEDandtradeIdis set.
The breakdown’s other statuses are AWAITING_FUNDING, EXPIRED and REVIEW_REQUIRED. You can also use the trade.created and 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}.
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. The customer cannot upload proof on a SELL. See Payment proof.
7. The customer releases
Release the USDT only after the fiat has actually arrived:
- Call
POST /v1/trades/{tradeId}/actions/{action}/executionwith thereleaseaction. The call takes no body and noIdempotency-Key, and every call returns a new prepared transaction. - Have the wallet sign it.
- Submit it with
POST /v1/transactions, which does need anIdempotency-Key.
The USDT goes to the merchant and the trade becomes RELEASED. The customer’s SELL side is free for a new trade. See release and Trade actions.
What can go wrong
Next steps
- Disputes for the timing and resolution rules.
- Trades and history to list a customer’s trades.
- Go-live checklist before you send real customers to production.