Skip to main content
This feature is in private alpha and the details are subject to change!
The rfq-fill route trades Backpack Securities tokens against USDC. It works differently from the other routes:
  • On the other routes, the trade completes in one transaction. A CONFIRMED status from POST /v1/execute means that the trade is done.
  • On rfq-fill, your transaction opens an order on Solana. A solver then fills the order. A CONFIRMED status means that the order is open, not that it filled.
To follow the order to its result, keep sending the same POST /v1/execute request until the response says done. To look up orders later, use GET /v1/orders. This recipe builds on the swap recipe. It uses the same headers, wallet, account, and signing code.

Before you start

  • Eligibility. Buying and selling tokenized stocks is only available to eligible users in supported jurisdictions. Eligibility restrictions apply under Backpack and Trek documentation and applicable law. Your use of the Sunrise API is subject to the Sunrise Terms of Service, including its Backpack/Trek Tokenized Equities Disclosures.
  • Market hours. When the stock’s market is closed, rfq-fill gives no quote. unavailableRoutes lists rfq-fill with the code market_closed. Another route can still quote the stock.

Step 1: Find a stock token

Call GET /v1/tokens?issuer=backpack_securities to list the Backpack Securities tokens. Use a token’s address as its mint and its decimals to convert amounts.
The list comes in pages. When tokensResult.data.pagination.nextCursor isn’t null, call the endpoint again with cursor set to it, and keep issuer unchanged. For a stock’s ticker, exchange, and fundamentals, see Stock data.

Step 2: Get a quote

rfq-fill quotes stock tokens against USDC only. To buy, pay with USDC. To sell, receive USDC. Choose the pair and the amount. Amounts are strings in base units.
Pay with USDC and receive the stock token. USDC has 6 decimals, so "10000000" means 10 USDC.
Then call POST /v1/quotes, as in the swap recipe:
For a stock token, the best quote can come from rfq-fill or from another route. Check quote.routeName:
  • rfq-fill: continue with this recipe.
  • Another route: the quote is a regular swap, and CONFIRMED means that the trade is done. Execute it as in the swap recipe, or skip it.
On a buy, the stock amount rounds down to a size that the solver can deliver. Thus, quote.fromAmount can be less than the USDC that you asked for. Show the quote’s amounts to your user, not the requested amount. quote.estimatedDurationMs estimates how long the order takes to complete after you execute it. To add your own fee to the trade, see Integrator fees and attribution.

Step 3: Sign the quote

Execute the quote before quote.expiresAt, about 10 seconds after you get it. If the quote expires, get a new quote. If you show your user a live price, get a new quote before expiresAt passes. Sign the quote’s transaction as in step 4 of the swap recipe.
Send the signed transaction only to POST /v1/execute. Sunrise sends it to Solana. Don’t broadcast it yourself or through your own RPC nodes: a transaction that you broadcast yourself won’t land.The quote’s transaction is a Solana v1 transaction. Sign it with a wallet or library that supports v1 transactions, and sign it exactly as returned. A transaction that’s rebuilt as a legacy or v0 transaction is rejected.

Step 4: Execute and track the order status

Call POST /v1/execute with the signed transaction, quoteId, routeName, and providerRequestId. Send the same request again until data.done is true. The order can take a few seconds to fill, so wait between requests.
Each response has status and done:
  • SUBMITTED: the transaction is still landing. done is false.
  • CONFIRMED: the transaction landed and the order is open. data.orders lists the order. It can be missing for a moment after the transaction lands, while done is still false. done turns true once every order is final.
What happens after CONFIRMED depends on the side:
  • Buy. The order holds your USDC. Your wallet doesn’t hold the stock token yet. A solver fills the order shortly after, so done stays false until then.
  • Sell. The solver pays you USDC in the same transaction that opens the order. Thus, the order is usually FILLED and done is true on the first CONFIRMED response. It can still show OPEN once, so keep the same loop.
An order usually reaches a final status within about quote.estimatedDurationMs. Show that estimate to your user. This example sends the request every 2 seconds, up to 300 times, in case the order takes longer. If the order still has no final status, it stays open. Send the same request again later. Sending the same request again is safe: a signed transaction is executed at most once. The solver can cancel an open order at any time. After the on-chain TTL, any wallet can cancel it. The refund always goes to your wallet. As of October 26, 2026, the TTL is 9,000 slots from when the order opens, about 30 minutes. The Sunrise API has no cancel endpoint.

Step 5: Handle the result

Each order in data.orders has orderId, type, status, and details. orderId is the quote’s providerRequestId. On rfq-fill, type is rfq. status decides which transaction fields details holds. “The input” is the token that you paid with: USDC for a buy, the stock token for a sell.
The fill statuses can give two amounts as strings of raw base units:
  • deliveredAmountRaw: the output token amount that the fill delivered to your wallet.
  • returnedAmountRaw: the input amount that the fill returned to your wallet.
POST /v1/execute can show a partial fill as FILLED. To get the final status and amounts, use step 6. When an amount is not in details, examine fillTxHash in a Solana explorer.
For your user, CANCELLED, REFUNDED, and FAILED all mean the same thing: no trade happened, and the input is in their wallet. They differ only in which transaction to link to.

Step 6: Get order status later

Two endpoints read orders after execute, in the same { orderId, type, status, details } shape:
  • GET /v1/orders?walletAddress= lists a wallet’s orders, newest placed first.
  • GET /v1/orders/{txHash} returns the orders that one transaction opened, filled, or cancelled.
Both need the same API key access as executing on rfq-fill. A key without it gets 403 forbidden.
Orders show up on these endpoints about a minute after their transaction lands. Thus, they never show PENDING or FAILED orders. To follow an order that you just placed, use the execute loop in step 4, which reads orders live.Orders opened before these endpoints launched aren’t listed.

List a wallet’s orders

Pass the wallet as walletAddress. To list only one status, add status: OPEN, FILLED, PARTIALLY_FILLED, CANCELLED, or REFUNDED. A page holds up to 100 orders. When pagination.nextCursor isn’t null, call the endpoint again with cursor set to it, and keep walletAddress and status unchanged.

Find the orders of a transaction

Pass the hash of any of the order’s transactions: its openTxHash, fillTxHash, or cancelTxHash. The response lists every order that the transaction touched.
A 404 with not_found means that no order is listed for the transaction. Within the first minute after it lands, that can just mean that the order isn’t listed yet.
An order fills at most once. A partial fill is final: your wallet gets less than the quote’s toAmount, and the rest of the input comes back in the same transaction. These endpoints show a partial fill as PARTIALLY_FILLED, with the returned input in returnedAmountRaw.

Errors

POST /v1/quotes, POST /v1/execute, and the orders endpoints return the errors in Errors. A 503 with execute_status_unknown means the API couldn’t find out whether the transaction was sent. Send the same request again. A 502 with bad_gateway that repeats won’t clear by itself: stop retrying and report it with the response’s requestId. For the full request and response schemas, see the API specification.