> For the complete documentation index, see [llms.txt](https://bound-1.gitbook.io/bound-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bound-1.gitbook.io/bound-docs/developer-guide/api-reference/bet.md).

# Bet

Place, execute, and exit bets

## Bound constraints

> Per-asset constraints so you can validate bounds without a \`/quote\` round-trip.\
> Same source as the server-side gates, so values never drift.\
> \
> \`coin\` is optional and defaults to the launch asset.\
> URL-encode HIP-3 names: \`?coin=xyz%3ATSLA\`, not \`?coin=xyz:TSLA\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Boundary Perps API","version":"1.0.0"},"tags":[{"name":"Bet","description":"Place, execute, and exit bets"}],"servers":[{"url":"https://api.bound.exchange","description":"Production"},{"url":"https://testnet-api.bound.exchange","description":"Testnet"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT access token from `POST /auth/authenticate`"}},"schemas":{"ApiResponse_Limits":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Limits"}}},"Limits":{"type":"object","required":["coin","mark","maxLeverage","maxBoundDistance","minAgainstDistance"],"properties":{"coin":{"type":"string"},"mark":{"type":"string","description":"Snapshot mark price at fetch time"},"maxLeverage":{"type":"integer"},"maxBoundDistance":{"type":"string","description":"Each bound must be within this fraction of mark (e.g. \"0.1\" = 10%)"},"minAgainstDistance":{"type":"object","required":["upper_first","lower_first"],"properties":{"upper_first":{"type":"string","description":"LONG: lower bound must be >= this fraction below mark"},"lower_first":{"type":"string","description":"SHORT: upper bound must be >= this fraction above mark"}}}}},"ErrorEnvelope":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code (26xxx for Boundary Perps errors)"},"message":{"type":"string","description":"Error message key"},"details":{"type":"string","description":"Human-readable detail (present on some errors)"}}}},"responses":{"ErrorResponse":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"paths":{"/api/which-first/limits":{"get":{"operationId":"getLimits","tags":["Bet"],"summary":"Bound constraints","description":"Per-asset constraints so you can validate bounds without a `/quote` round-trip.\nSame source as the server-side gates, so values never drift.\n\n`coin` is optional and defaults to the launch asset.\nURL-encode HIP-3 names: `?coin=xyz%3ATSLA`, not `?coin=xyz:TSLA`.\n","parameters":[{"name":"coin","in":"query","required":false,"schema":{"type":"string"},"description":"Asset name (e.g. BTC, ETH). URL-encode HIP-3 names."}],"responses":{"200":{"description":"Limit constraints for the asset","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Limits"}}}},"400":{"$ref":"#/components/responses/ErrorResponse"}}}}}}
```

## Price a bet

> Pure pricing preview. No wallet gates, no persistence, no side effects.\
> \
> \`wager\` is isolated margin, not order notional.\
> \`payoutMultiple\` is profit / wager. To show a total-return multiple, use \`1 + payoutMultiple\`.\
> \
> This endpoint deliberately does not enforce the configured wager range or Hyperliquid's\
> $10 minimum order notional. Treat \`/proposals\` as the authoritative gate.<br>

```json
{"openapi":"3.0.3","info":{"title":"Boundary Perps API","version":"1.0.0"},"tags":[{"name":"Bet","description":"Place, execute, and exit bets"}],"servers":[{"url":"https://api.bound.exchange","description":"Production"},{"url":"https://testnet-api.bound.exchange","description":"Testnet"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT access token from `POST /auth/authenticate`"}},"schemas":{"QuoteRequest":{"type":"object","required":["side","upperBound","lowerBound","wager"],"properties":{"side":{"type":"string","enum":["upper_first","lower_first"],"description":"Which bound you bet on being touched first"},"upperBound":{"type":"string","description":"Upper price boundary"},"lowerBound":{"type":"string","description":"Lower price boundary"},"wager":{"type":"string","description":"Isolated margin amount in USDC"},"coin":{"type":"string","description":"Asset name (e.g. BTC, ETH). Optional — defaults to the launch asset if omitted."}}},"ApiResponse_Construction":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Construction"}}},"Construction":{"type":"object","required":["direction","leverage","notional","coinSize","chosenBound","losingBound","takeProfitPrice","liquidationPrice","payoutMultiple","payoutEstimate","maxSlippage","entryPrice","wager"],"properties":{"direction":{"type":"string","enum":["long","short"]},"leverage":{"type":"integer","description":"Integer, rounded down"},"notional":{"type":"string","description":"leverage * wager"},"coinSize":{"type":"string"},"chosenBound":{"type":"string","description":"The bound you bet on — becomes take-profit"},"losingBound":{"type":"string","description":"The opposing bound — liquidation level"},"takeProfitPrice":{"type":"string"},"liquidationPrice":{"type":"string","description":"Pre-trade estimate at the mark"},"payoutMultiple":{"type":"string","description":"profit / wager (NOT total return — add 1 for total-return multiple)"},"payoutEstimate":{"type":"string","description":"Profit in USDC if won, before fees and funding"},"maxSlippage":{"type":"string"},"entryPrice":{"type":"string","description":"Mark price used to price"},"wager":{"type":"string"}}},"ErrorEnvelope":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code (26xxx for Boundary Perps errors)"},"message":{"type":"string","description":"Error message key"},"details":{"type":"string","description":"Human-readable detail (present on some errors)"}}}},"responses":{"ErrorResponse":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"paths":{"/api/which-first/quote":{"post":{"operationId":"createQuote","tags":["Bet"],"summary":"Price a bet","description":"Pure pricing preview. No wallet gates, no persistence, no side effects.\n\n`wager` is isolated margin, not order notional.\n`payoutMultiple` is profit / wager. To show a total-return multiple, use `1 + payoutMultiple`.\n\nThis endpoint deliberately does not enforce the configured wager range or Hyperliquid's\n$10 minimum order notional. Treat `/proposals` as the authoritative gate.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"description":"Construction preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Construction"}}}},"400":{"$ref":"#/components/responses/ErrorResponse"}}}}}}
```

## Create the bet

> Runs every gate, persists a \`proposed\` bet (which holds the per-asset slot),\
> and returns the actions to sign.\
> \
> Only one open bet per asset per account — including from a different wallet\
> on the same account. A retry while one is open returns \`26007\`.\
> \
> Enforces the configured wager range (default 10-100,000 USDC) and requires\
> \`price \* coinSize >= $10\` for both order wires.<br>

```json
{"openapi":"3.0.3","info":{"title":"Boundary Perps API","version":"1.0.0"},"tags":[{"name":"Bet","description":"Place, execute, and exit bets"}],"servers":[{"url":"https://api.bound.exchange","description":"Production"},{"url":"https://testnet-api.bound.exchange","description":"Testnet"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT access token from `POST /auth/authenticate`"}},"schemas":{"ProposalRequest":{"type":"object","required":["side","upperBound","lowerBound","wager","userAddress"],"properties":{"side":{"type":"string","enum":["upper_first","lower_first"]},"upperBound":{"type":"string"},"lowerBound":{"type":"string"},"wager":{"type":"string"},"coin":{"type":"string","description":"Optional — defaults to the launch asset if omitted"},"userAddress":{"type":"string","description":"EVM wallet that signs and owns the position"}}},"ApiResponse_ProposalResult":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProposalResult"}}},"ProposalResult":{"type":"object","required":["betId","state","proposalExpiresAt","construction","unsignedActions"],"properties":{"betId":{"type":"string"},"state":{"type":"string","enum":["proposed"]},"proposalExpiresAt":{"type":"string","format":"date-time","description":"Sign and execute BEFORE this time"},"construction":{"$ref":"#/components/schemas/Construction"},"unsignedActions":{"type":"array","items":{"$ref":"#/components/schemas/UnsignedAction"}}}},"Construction":{"type":"object","required":["direction","leverage","notional","coinSize","chosenBound","losingBound","takeProfitPrice","liquidationPrice","payoutMultiple","payoutEstimate","maxSlippage","entryPrice","wager"],"properties":{"direction":{"type":"string","enum":["long","short"]},"leverage":{"type":"integer","description":"Integer, rounded down"},"notional":{"type":"string","description":"leverage * wager"},"coinSize":{"type":"string"},"chosenBound":{"type":"string","description":"The bound you bet on — becomes take-profit"},"losingBound":{"type":"string","description":"The opposing bound — liquidation level"},"takeProfitPrice":{"type":"string"},"liquidationPrice":{"type":"string","description":"Pre-trade estimate at the mark"},"payoutMultiple":{"type":"string","description":"profit / wager (NOT total return — add 1 for total-return multiple)"},"payoutEstimate":{"type":"string","description":"Profit in USDC if won, before fees and funding"},"maxSlippage":{"type":"string"},"entryPrice":{"type":"string","description":"Mark price used to price"},"wager":{"type":"string"}}},"UnsignedAction":{"type":"object","required":["kind","action"],"properties":{"kind":{"type":"string","enum":["update_leverage","entry","cancel","cash_out_close"]},"action":{"$ref":"#/components/schemas/HlAction"}}},"HlAction":{"type":"object","additionalProperties":true,"description":"Opaque Hyperliquid action object. Bound builds it; you sign it byte for byte\nand never edit it. Bound re-derives the expected body when you relay it back.\n"},"ErrorEnvelope":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code (26xxx for Boundary Perps errors)"},"message":{"type":"string","description":"Error message key"},"details":{"type":"string","description":"Human-readable detail (present on some errors)"}}}},"responses":{"ErrorResponse":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"paths":{"/api/which-first/proposals":{"post":{"operationId":"createProposal","tags":["Bet"],"summary":"Create the bet","description":"Runs every gate, persists a `proposed` bet (which holds the per-asset slot),\nand returns the actions to sign.\n\nOnly one open bet per asset per account — including from a different wallet\non the same account. A retry while one is open returns `26007`.\n\nEnforces the configured wager range (default 10-100,000 USDC) and requires\n`price * coinSize >= $10` for both order wires.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalRequest"}}}},"responses":{"200":{"description":"Proposal with unsigned actions to sign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_ProposalResult"}}}},"400":{"$ref":"#/components/responses/ErrorResponse"}}}}}}
```

## Relay the signed bundle

> The only signed bodies Bound relays. Sign the \`unsignedActions\` from \`/proposals\` (entry)\
> or \`/cash-out\`, keep each original \`kind\`, and send them in order.\
> \
> \*\*Relay-once:\*\* \`26028\` fires when order ids are already recorded, another relay is in flight,\
> or a retry found venue evidence. Never re-sign — a new nonce defeats Hyperliquid's dedup.\
> \
> \*\*\`26025\`\*\* is the only definitive "nothing landed" verdict — retrying after it is safe.<br>

```json
{"openapi":"3.0.3","info":{"title":"Boundary Perps API","version":"1.0.0"},"tags":[{"name":"Bet","description":"Place, execute, and exit bets"}],"servers":[{"url":"https://api.bound.exchange","description":"Production"},{"url":"https://testnet-api.bound.exchange","description":"Testnet"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT access token from `POST /auth/authenticate`"}},"parameters":{"BetId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Bet ID (24-char hex string)"}},"schemas":{"ExecuteRequest":{"type":"object","required":["actions"],"properties":{"actions":{"type":"array","items":{"$ref":"#/components/schemas/SignedAction"},"description":"Signed actions in order (from proposals or cash-out)"}}},"SignedAction":{"type":"object","required":["kind","action","nonce","signature"],"properties":{"kind":{"type":"string","enum":["update_leverage","entry","cancel","cash_out_close","take_profit","approve_builder_fee","set_referrer","withdraw"],"description":"Action kind — must match the unsigned action's kind. Entry bundles use update_leverage + entry; cash-out bundles use cancel + cash_out_close."},"action":{"$ref":"#/components/schemas/HlAction"},"nonce":{"type":"number","description":"Monotonic nonce in milliseconds"},"signature":{"description":"Hex-encoded signature string, or an {r, s, v} object"}}},"HlAction":{"type":"object","additionalProperties":true,"description":"Opaque Hyperliquid action object. Bound builds it; you sign it byte for byte\nand never edit it. Bound re-derives the expected body when you relay it back.\n"},"ApiResponse_Bet":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Bet"}}},"Bet":{"type":"object","required":["_id","coin","evmAddress","side","upperBound","lowerBound","wager","construction","state","proposalExpiresAt","venueRefs","actuals","createdAt","updatedAt"],"properties":{"_id":{"type":"string","description":"Bet ID (the list returns `_id`; only /proposals returns `betId`)"},"accountId":{"type":"string","description":"Internal account identifier"},"coin":{"type":"string"},"evmAddress":{"type":"string","description":"Stored lower-cased"},"side":{"type":"string","enum":["upper_first","lower_first"]},"upperBound":{"type":"string"},"lowerBound":{"type":"string"},"wager":{"type":"string","description":"Isolated margin, USDC"},"construction":{"$ref":"#/components/schemas/BetConstruction"},"state":{"type":"string","enum":["proposed","active","won","lost","cashed_out","voided","aborted"]},"proposalExpiresAt":{"type":"string","format":"date-time"},"activatedAt":{"type":"string","format":"date-time","nullable":true,"description":"Entry fill verified -> active"},"resolvedAt":{"type":"string","format":"date-time","nullable":true,"description":"Reached won / lost / cashed_out / voided"},"abortReason":{"type":"string","nullable":true,"enum":["proposal_expired","user_abandoned",null]},"voidReason":{"type":"string","nullable":true,"enum":["tp_cancelled","margin_changed","position_closed","position_resized","other",null]},"cashOutPending":{"type":"boolean","description":"True once a cash-out was requested"},"boundBreachFlagged":{"type":"boolean","description":"The real fill put liquidation inside the losing bound"},"venueRefs":{"$ref":"#/components/schemas/VenueRefs"},"actuals":{"$ref":"#/components/schemas/BetActuals"},"relayClaimedAt":{"type":"string","format":"date-time","nullable":true,"description":"Internal relay-claim timestamp"},"createdAt":{"type":"number","description":"Epoch ms (NOT an ISO string)"},"updatedAt":{"type":"number","description":"Epoch ms — bumped by every write"},"deletedAt":{"type":"string","format":"date-time","nullable":true,"description":"Internal — soft-delete timestamp from BaseEntity. May stop being serialized."}}},"BetConstruction":{"type":"object","description":"Construction snapshot recorded at proposal time. Contains the same pricing fields\nas Construction (except `wager`, which is stored at the top-level Bet) plus 4\nvenue-snapshot fields.\n","properties":{"direction":{"type":"string","enum":["long","short"]},"leverage":{"type":"integer"},"notional":{"type":"string"},"coinSize":{"type":"string"},"chosenBound":{"type":"string"},"losingBound":{"type":"string"},"takeProfitPrice":{"type":"string"},"liquidationPrice":{"type":"string"},"payoutMultiple":{"type":"string"},"payoutEstimate":{"type":"string"},"maxSlippage":{"type":"string"},"entryPrice":{"type":"string"},"assetIndex":{"type":"integer"},"szDecimals":{"type":"integer"},"maintenanceMarginFraction":{"type":"string"},"builderFeeRate":{"type":"string"}}},"VenueRefs":{"type":"object","properties":{"entryOid":{"type":"string","nullable":true},"takeProfitOid":{"type":"string","nullable":true,"description":"null for up to one monitor cycle (~30s) right after execute"},"closeOid":{"type":"string","nullable":true}}},"BetActuals":{"type":"object","description":"Post-fill data — every field null until the venue event lands","properties":{"actualEntryPrice":{"type":"string","nullable":true},"actualLiquidationPrice":{"type":"string","nullable":true,"description":"On-venue value — use this once active"},"actualPayoutMultiple":{"type":"string","nullable":true},"actualCoinSize":{"type":"string","nullable":true},"actualMarginUsed":{"type":"string","nullable":true},"fundingAtActivation":{"type":"string","nullable":true},"finalProceeds":{"type":"string","nullable":true,"description":"GROSS realized PnL — before fees and funding. Not the user's result."},"feesPaid":{"type":"string","nullable":true,"description":"Close-side exchange fee"},"fundingPaid":{"type":"string","nullable":true,"description":"Signed; negative = paid"},"netProceeds":{"type":"string","nullable":true,"description":"Authoritative NET (finalProceeds - fees + funding). Nullable — best-effort.\nWhen null, recompute from Hyperliquid userFills + userFunding.\n"}}},"ErrorEnvelope":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code (26xxx for Boundary Perps errors)"},"message":{"type":"string","description":"Error message key"},"details":{"type":"string","description":"Human-readable detail (present on some errors)"}}}},"responses":{"ErrorResponse":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"paths":{"/api/which-first/{id}/execute":{"post":{"operationId":"executeBet","tags":["Bet"],"summary":"Relay the signed bundle","description":"The only signed bodies Bound relays. Sign the `unsignedActions` from `/proposals` (entry)\nor `/cash-out`, keep each original `kind`, and send them in order.\n\n**Relay-once:** `26028` fires when order ids are already recorded, another relay is in flight,\nor a retry found venue evidence. Never re-sign — a new nonce defeats Hyperliquid's dedup.\n\n**`26025`** is the only definitive \"nothing landed\" verdict — retrying after it is safe.\n","parameters":[{"$ref":"#/components/parameters/BetId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteRequest"}}}},"responses":{"200":{"description":"Updated bet after execution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Bet"}}}},"400":{"$ref":"#/components/responses/ErrorResponse"}}}}}}
```

## Exit early

> Returns an unsigned bundle that cancels the resting take-profit and closes the position\
> reduce-only. Only for an \`active\` bet.\
> \
> Sign both actions and send them to \`POST /{id}/execute\` — same as the entry bundle.\
> Repeating a cash-out is a no-op; a bet that is no longer active returns \`26012\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Boundary Perps API","version":"1.0.0"},"tags":[{"name":"Bet","description":"Place, execute, and exit bets"}],"servers":[{"url":"https://api.bound.exchange","description":"Production"},{"url":"https://testnet-api.bound.exchange","description":"Testnet"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT access token from `POST /auth/authenticate`"}},"parameters":{"BetId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Bet ID (24-char hex string)"}},"schemas":{"ApiResponse_CashOutResult":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CashOutResult"}}},"CashOutResult":{"type":"object","required":["betId","unsignedActions"],"properties":{"betId":{"type":"string"},"unsignedActions":{"type":"array","items":{"$ref":"#/components/schemas/UnsignedAction"},"minItems":2,"maxItems":2,"description":"[cancel, cash_out_close]"}}},"UnsignedAction":{"type":"object","required":["kind","action"],"properties":{"kind":{"type":"string","enum":["update_leverage","entry","cancel","cash_out_close"]},"action":{"$ref":"#/components/schemas/HlAction"}}},"HlAction":{"type":"object","additionalProperties":true,"description":"Opaque Hyperliquid action object. Bound builds it; you sign it byte for byte\nand never edit it. Bound re-derives the expected body when you relay it back.\n"},"ErrorEnvelope":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Error code (26xxx for Boundary Perps errors)"},"message":{"type":"string","description":"Error message key"},"details":{"type":"string","description":"Human-readable detail (present on some errors)"}}}},"responses":{"ErrorResponse":{"description":"Error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"paths":{"/api/which-first/{id}/cash-out":{"post":{"operationId":"cashOut","tags":["Bet"],"summary":"Exit early","description":"Returns an unsigned bundle that cancels the resting take-profit and closes the position\nreduce-only. Only for an `active` bet.\n\nSign both actions and send them to `POST /{id}/execute` — same as the entry bundle.\nRepeating a cash-out is a no-op; a bet that is no longer active returns `26012`.\n","parameters":[{"$ref":"#/components/parameters/BetId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Cash-out bundle with unsigned actions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_CashOutResult"}}}},"400":{"$ref":"#/components/responses/ErrorResponse"}}}}}}
```
