> For the complete documentation index, see [llms.txt](https://docs.radfi.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.radfi.co/dev/api-endpoints/which-first.md).

# Which First

## GET /api/which-first/limits

> Per-asset bound limits (min chosen-against distance per side + max bound distance) so the FE can pre-validate bounds without a /quote round-trip.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first/limits":{"get":{"operationId":"WhichFirstController_limits","summary":"Per-asset bound limits (min chosen-against distance per side + max bound distance) so the FE can pre-validate bounds without a /quote round-trip.","parameters":[{"name":"coin","required":false,"in":"query","description":"Canonical venue asset name; defaults to launch asset","schema":{"maxLength":128,"type":"string"}}],"responses":{"200":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/quote

> Price a bet — leverage, liquidation, payout estimate. Not persisted.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstQuoteRequestDto":{"type":"object","properties":{"side":{"type":"string","enum":["upper_first","lower_first"]},"upperBound":{"type":"string","description":"Upper bound price"},"lowerBound":{"type":"string","description":"Lower bound price"},"wager":{"type":"string","description":"Positive wager / isolated margin in USDC"},"coin":{"type":"string","maxLength":128,"description":"Canonical venue asset name (for example BTC or xyz:TSLA); defaults to the launch asset"}},"required":["side","upperBound","lowerBound","wager"]}}},"paths":{"/api/which-first/quote":{"post":{"operationId":"WhichFirstController_quote","summary":"Price a bet — leverage, liquidation, payout estimate. Not persisted.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstQuoteRequestDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/proposals

> Create a PROPOSED bet: validates all gates, returns unsigned actions for the user to sign — then send the signed actions back to POST /which-first/:id/execute (the BE relays them to HL). One open bet per asset (a retry on an asset with an open bet returns existingExposure).

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstCreateProposalDto":{"type":"object","properties":{"side":{"type":"string","enum":["upper_first","lower_first"]},"upperBound":{"type":"string","description":"Upper bound price"},"lowerBound":{"type":"string","description":"Lower bound price"},"wager":{"type":"string","description":"Positive wager / isolated margin in USDC"},"coin":{"type":"string","maxLength":128,"description":"Canonical venue asset name (for example BTC or xyz:TSLA); defaults to the launch asset"},"userAddress":{"type":"string","description":"EVM wallet (must belong to the account) that signs/owns the Hyperliquid position"}},"required":["side","upperBound","lowerBound","wager","userAddress"]}}},"paths":{"/api/which-first/proposals":{"post":{"operationId":"WhichFirstController_createProposal","summary":"Create a PROPOSED bet: validates all gates, returns unsigned actions for the user to sign — then send the signed actions back to POST /which-first/:id/execute (the BE relays them to HL). One open bet per asset (a retry on an asset with an open bet returns existingExposure).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstCreateProposalDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/builder-fee-approval

> Unsigned one-time builder-fee approval — sign with the main wallet before the first bet, then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay).

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first/builder-fee-approval":{"post":{"operationId":"WhichFirstController_builderFeeApproval","summary":"Unsigned one-time builder-fee approval — sign with the main wallet before the first bet, then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay).","parameters":[],"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/referral

> Unsigned one-time referral opt-in (setReferrer) — sign at first trade, then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay). Returns the \`{ kind, action }\` IUnsignedVenueAction, or \`null\` when no code is configured / the wallet is already referred. Best-effort: decoupled from betting, never blocks a bet.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstReferralRequestDto":{"type":"object","properties":{"userAddress":{"type":"string","description":"EVM wallet (must belong to the account) that will sign setReferrer"}},"required":["userAddress"]}}},"paths":{"/api/which-first/referral":{"post":{"operationId":"WhichFirstController_referral","summary":"Unsigned one-time referral opt-in (setReferrer) — sign at first trade, then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay). Returns the `{ kind, action }` IUnsignedVenueAction, or `null` when no code is configured / the wallet is already referred. Best-effort: decoupled from betting, never blocks a bet.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstReferralRequestDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/deposit

> Unsigned HyperEVM deposit PLAN funding the wallet's HyperCore account: \`txs\` = \[approve, deposit] on USDC's deposit contract. SIGN AND BROADCAST BOTH txs IN ORDER on HyperEVM yourself (NORMAL eth\_sendTransaction, NOT the HL SDK; \`rpcUrl\` is in the plan) from the SAME bound wallet you bet with — no BE broadcast.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstDepositRequestDto":{"type":"object","properties":{"userAddress":{"type":"string","description":"EVM wallet (must belong to the account) that will sign + broadcast the deposit"},"amount":{"type":"string","description":"USDC amount to deposit (positive, at most 6 decimals)"}},"required":["userAddress","amount"]}}},"paths":{"/api/which-first/deposit":{"post":{"operationId":"WhichFirstController_deposit","summary":"Unsigned HyperEVM deposit PLAN funding the wallet's HyperCore account: `txs` = [approve, deposit] on USDC's deposit contract. SIGN AND BROADCAST BOTH txs IN ORDER on HyperEVM yourself (NORMAL eth_sendTransaction, NOT the HL SDK; `rpcUrl` is in the plan) from the SAME bound wallet you bet with — no BE broadcast.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstDepositRequestDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/withdraw

> Unsigned sendAsset withdraw (USDC HyperCore → HyperEVM, credited to the SAME wallet — the deposit's mirror). Sign with signUserSignedAction (EIP-712 SendAsset types; add \`nonce\` INTO the action), then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay). Positive amount, ≤6 decimals (no fixed minimum); small proportional fee.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstWithdrawRequestDto":{"type":"object","properties":{"userAddress":{"type":"string","description":"EVM wallet (must belong to the account) whose HyperCore USDC is withdrawn — it also RECEIVES the USDC on HyperEVM"},"amount":{"type":"string","description":"USDC amount to withdraw (positive, at most 6 decimals)"}},"required":["userAddress","amount"]}}},"paths":{"/api/which-first/withdraw":{"post":{"operationId":"WhichFirstController_withdraw","summary":"Unsigned sendAsset withdraw (USDC HyperCore → HyperEVM, credited to the SAME wallet — the deposit's mirror). Sign with signUserSignedAction (EIP-712 SendAsset types; add `nonce` INTO the action), then the FE POSTs the signed envelope DIRECTLY to HL /exchange (no BE relay). Positive amount, ≤6 decimals (no fixed minimum); small proportional fee.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstWithdrawRequestDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## GET /api/which-first

> Caller's bets (paginated). Filter via query-string: \`state\_eq\`, \`evmAddress\_eq\`.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first":{"get":{"operationId":"WhichFirstController_paginate","summary":"Caller's bets (paginated). Filter via query-string: `state_eq`, `evmAddress_eq`.","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort field and order. Use - prefix for descending. Example: -createdAt, createdAt","schema":{"type":"string"}},{"name":"select","required":false,"in":"query","description":"Fields to select (comma separated). Use + prefix to include hidden fields. Example: name,status,+holders","schema":{"type":"string"}},{"name":"populate","required":false,"in":"query","description":"Relations to populate (comma separated). Example: wallet,token","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["which-first"]}}}}
```

## GET /api/which-first/open-interest

> Bound's open Which First exposure (ACTIVE bets only): totals + per-coin breakdown. Admin only. Optional half-open window on \`activatedAt\` via \`from\`/\`to\` (ISO-8601, millisecond precision; each bound independent, \`from\` inclusive, \`to\` exclusive). NOTE: notional is the REQUESTED notional priced at proposal time, NOT mark-to-market.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first/open-interest":{"get":{"operationId":"WhichFirstController_openInterest","summary":"Bound's open Which First exposure (ACTIVE bets only): totals + per-coin breakdown. Admin only. Optional half-open window on `activatedAt` via `from`/`to` (ISO-8601, millisecond precision; each bound independent, `from` inclusive, `to` exclusive). NOTE: notional is the REQUESTED notional priced at proposal time, NOT mark-to-market.","parameters":[{"name":"from","required":false,"in":"query","description":"ISO-8601 window start on activatedAt, INCLUSIVE (millisecond precision kept)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"ISO-8601 window end on activatedAt, EXCLUSIVE (millisecond precision kept)","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["which-first"]}}}}
```

## GET /api/which-first/{id}

> Get one bet (owner only).

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first/{id}":{"get":{"operationId":"WhichFirstController_getBet","summary":"Get one bet (owner only).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/{id}/cash-out

> Prepare an unsigned cash-out bundle (cancel TP + reduce-only close) for an Active bet.

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}}},"paths":{"/api/which-first/{id}/cash-out":{"post":{"operationId":"WhichFirstController_cashOut","summary":"Prepare an unsigned cash-out bundle (cancel TP + reduce-only close) for an Active bet.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```

## POST /api/which-first/{id}/execute

> Relay the client-signed bet actions (entry bundle or cash-out bundle) to HL /exchange IN ORDER; records the resulting oids (entry→\[entryOid, takeProfitOid], cash\_out\_close→\[closeOid]) so the monitor can attach. Non-custodial — the BE only forwards the signed bodies. State-gated: an entry only relays for a still-open, unexpired proposal (else 26019 notProposed / 26011 proposalExpired), a cash-out only for an ACTIVE bet (26012 notActive).

```json
{"openapi":"3.0.0","info":{"title":"Radfi API","version":"1.0"},"servers":[{"url":"https://api.radfi.co","description":"Production"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WhichFirstExecuteBetDto":{"type":"object","properties":{"actions":{"description":"Signed actions to relay to HL /exchange IN ORDER (entry bundle or cash-out bundle)","type":"array","items":{"$ref":"#/components/schemas/WhichFirstSignedBetActionDto"}}},"required":["actions"]},"WhichFirstSignedBetActionDto":{"type":"object","properties":{"action":{"type":"object","description":"The signed HL action object (opaque, passed through verbatim)"},"nonce":{"type":"number","description":"The nonce (ms) the signature covers"},"signature":{"type":"object","description":"Client signature over action + nonce (opaque; string or {r,s,v})"},"kind":{"type":"string","enum":["update_leverage","entry","take_profit","cash_out_close","cancel","approve_builder_fee","set_referrer","withdraw"],"description":"Which unsigned action this is (drives oid mapping)"}},"required":["action","nonce","signature","kind"]}}},"paths":{"/api/which-first/{id}/execute":{"post":{"operationId":"WhichFirstController_executeBet","summary":"Relay the client-signed bet actions (entry bundle or cash-out bundle) to HL /exchange IN ORDER; records the resulting oids (entry→[entryOid, takeProfitOid], cash_out_close→[closeOid]) so the monitor can attach. Non-custodial — the BE only forwards the signed bodies. State-gated: an entry only relays for a still-open, unexpired proposal (else 26019 notProposed / 26011 proposalExpired), a cash-out only for an ACTIVE bet (26012 notActive).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhichFirstExecuteBetDto"}}}},"responses":{"201":{"description":""}},"tags":["which-first"]}}}}
```
