Skip to main content
Skip to main content

Your first trade

This is the golden path for a taker: one manually accepted RFQ, driven over the REST API with live updates from the WebSocket API. The calls below run against the production host, rfq-api.silhouette.exchange; see Signing requests for the hmac_headers helper the REST calls below rely on.

1. Mint credentials​

Follow the login flow: request a nonce with POST /v1/auth/challenge, sign the SIWE message with your wallet, and submit it to POST /v1/auth/api-keys. Keep the returned accessKey and secret; the secret is shown once.

2. Connect the socket and subscribe​

Open wss://rfq-api.silhouette.exchange/v1/rfq/ws, authenticate with an auth frame (the credential pair signs a canonical GET /v1/rfq/ws), then subscribe to your RFQ statuses and the competing quotes on them:

{ "kind": "auth", "accessKey": "<access-key>", "signedAt": 1769600000000, "signature": "<base64-hmac>" }

The server confirms with { "kind": "health", ... }. Then:

{ "kind": "subscribe", "topic": "rfqStatus" }
{ "kind": "subscribe", "topic": "quotes" }

Each is acknowledged with a subscribed frame echoing the topic.

3. Submit the RFQ over REST​

Buy 0.5 TSLAX for at most 1000 USDC, with a 30-second quote window:

curl https://rfq-api.silhouette.exchange/v1/rfq/requests \
--request POST \
--header "Content-Type: application/json" \
# plus the three signed headers from hmac_headers
--data '{
"instrumentId": "TSLAX-USDC-SPOT",
"side": "BUY",
"baseQty": "0.5",
"quoteLimit": "1000",
"windowMs": 30000,
"idempotencyKey": "0193b6f1-7c10-7d6c-8000-abc123456789"
}'

quoteLimit is the most you will pay for baseQty, taker fee included. Some markets also set a smallest order value they take, published on the instrument as minQuoteNotional: on a BUY your limit has to cover that figure plus the fee charged on it, or the call returns 400 BELOW_MIN_NOTIONAL with details.minQuoteNotional naming what to resize from. Read the field from GET /v1/rfq/instruments before you send; a market that sets no minimum omits it. See Minimum order value.

The 202 response carries the RFQ's identity and the auction deadline the clamp settled on:

{ "rfqId": "rfq_...", "status": "PENDING", "auctionEndsAt": 1769600030000 }

4. Watch the quotes stream in​

As makers respond, the quotes subscription pushes a quoteUpdate frame per competing quote. takerTotal is the all-in amount you would pay, taker fee included — it is the figure your quoteLimit bounds, so a quote wins only if its takerTotal is at or under it:

{
"kind": "quoteUpdate",
"rfqId": "rfq_...",
"quoteId": "qt_...",
"status": "SUBMITTED",
"takerTotal": "992.50",
"receivedAt": 1769600005000,
"acceptableForMs": 30000
}

acceptableForMs is the maker's own window, counted from receivedAt; it is absent when the maker named none, and the quote then stands until the auction closes.

5. Accept the best quote​

Before the auction ends, accept over REST:

curl https://rfq-api.silhouette.exchange/v1/rfq/requests/rfq_.../accept \
--request POST \
--header "Content-Type: application/json" \
# plus the three signed headers from hmac_headers
--data '{ "quoteId": "qt_..." }'

6. Watch it settle​

The rfqStatus subscription streams each transition. Acceptance moves the RFQ to QUOTED while settlement is in progress, and settlement completes it:

{ "kind": "rfqStatus", "id": "rfq_...", "status": "SETTLED", "txHash": "0x..." }

A trade is an RFQ in SETTLED, carrying its settlement transaction hash. If nothing conforming arrived, or you let the deadline pass, the RFQ ends FAILED instead and any locked funds are released; CANCELLED is reserved for a request you cancelled yourself, which you may do while it is PENDING and before its auctionEndsAt. The lifecycle table covers every outcome.

That is the whole loop: REST for every action, the socket for everything that happens in between. From here, read balances with GET /v1/rfq/balances (or subscribe to balances), and explore the full surface in the API Reference.