# bskts API

> Non-custodial API for bskts: tokenized index baskets of Stock Tokens and crypto
> on Robinhood Chain (EVM, chainId 4663). Reads are open JSON, no key. /v1/tx/*
> return UNSIGNED transactions that your wallet signs; the API never holds keys
> or funds. Base: https://api.bskts.xyz

## The trading loop
1. Pick: GET /v1/markets?fields=ticker,nav,change24h,buyCostBps&prices=0
2. Check: GET /v1/wallet/{address} (USDG to spend; gasOk = enough ETH for gas)
3. Buy: POST /v1/tx/buy {ticker, amountUsdg, address}. Send `approval` if not
   null and wait for it to be mined, then send `tx`.
4. Hold: GET /v1/positions/{address} (value, PnL, walletSharesWei)
5. Sell: POST /v1/tx/sell {ticker, address, shares:"max"} (or fraction:0.5).
   Same approval-then-tx order. USDG arrives in the same transaction.

Every /v1/tx response has `simulation`: {ok:true} = the dry run as your address
passed; {ok:false, reason} = it would revert, do not send; {ok:null} = not run
(send the approval first). `warnings` flags e.g. too little USDG.

## Setup
- Chain: Robinhood Chain, chainId 4663, RPC https://rpc.mainnet.chain.robinhood.com,
  explorer https://robinhoodchain.blockscout.com
- Gas: every transaction from a plain wallet pays gas in ETH on chain 4663.
- Cash: USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 (6 decimals). Baskets are bought and sold for USDG.
- Tokens: use only the addresses this API returns. The chain has impostor
  tokens with the same symbols.
- Baskets (15): DIGI64, MAG6, INDEX2, FRONTIER, CRYPTOEQ, OPUS, FABLE, ORBIT, MAXBETA, SILICON, ETHSTACK, NATIVE, HARD, BEDROCK, TURBO.

## Units
- amountUsdg, nav, triggerNav, stopNav, tpNav, value, PnL: plain dollars.
- *Wei fields, shares in requests, amountInUsdg, minShares, expectedUsdg,
  minOutUsdg, feeUsdg: integer strings in base units (shares 1e18, USDG 1e6).
- Send sharesWei / walletSharesWei back as-is. Never float x 1e18: it rounds up
  past your balance and the transaction reverts.
- change24h, change, maxDrawdown, volatility: fractions (0.012 = +1.2%).
- buyCostBps, slippageBps: basis points (100 = 1%).

## Reads
- GET /v1/markets?fields=&prices=0: marks for every basket (compact).
- GET /v1/baskets?fields=: + name, thesis, weights, risk note. /{ticker}: + constituents.
- GET /v1/nav/{ticker}/history?res=1m|1h|1d&days=N&format=summary|close|ohlc.
  summary (change, high, low, maxDrawdown, volatility) is ~1% of ohlc.
- GET /v1/wallet/{address}: ETH, USDG, basket shares held, gasOk.
- GET /v1/positions/{address}: holdings, avg entry, unrealized / realized PnL.
- GET /v1/orders/{address}: open stop / take-profit / bracket exits and limit buys.
- GET /v1/stats · GET /v1/strategies/{address} · GET /v1/jobs (below)

## Build (POST JSON; add "simulate":false to skip the dry run)
- /v1/tx/buy {ticker, amountUsdg, address, slippageBps?=50}
- /v1/tx/sell {ticker, address, shares: "<wei>"|"max", fraction?, slippageBps?=50}
- /v1/tx/limit {ticker, amountUsdg, address, triggerNav?, expiryDays?=30}: a
  resting buy that fills when the 10-minute average NAV <= triggerNav (none =
  market). USDG is escrowed until it fills or you cancel.
- /v1/tx/exit {ticker, address, shares, stopNav?, tpNav?, expiryDays?}:
  stop-loss, take-profit or both (one cancels the other), on the 10-minute
  average NAV. Shares are escrowed until it fills or you cancel.
- /v1/tx/cancel {orderId, address?}: withdraw an open order (ids from /v1/orders).
- /v1/tx/redeem {ticker, address, shares}: take the underlying tokens instead of USDG.

## Before you trade
- Read buyCostBps: the real cost of a buy (pool fees + price impact), on top of
  the 0.20% mint fee. Selling costs about the same plus the 0.20% sell fee.
- Read `risk` (GET /v1/baskets) for the higher-risk baskets.
- Automated orders (limit, exit) are unavailable for some baskets (TURBO today):
  422 NO_REFERENCE. Market buys and sells still work.
- Fees: mint 0.20%, sell 0.20%, in-kind redeem free, management 0.50-0.95%/yr in
  NAV. Order fills pay the filler max($0.25, 0.15%), at most 1%.

## Errors
JSON {error, code, retryable}. Branch on code:
BAD_REQUEST, UNKNOWN_BASKET, NOT_FOUND, NO_ROUTE (basket not tradable via the
API now), NO_REFERENCE, ORDER_NOT_OPEN, INSUFFICIENT_SHARES, INSUFFICIENT_FUNDS,
SESSION_INACTIVE (owner approval/setup needed), SESSION_LIMIT (owner limits), PAUSED (retry
later), UPSTREAM / SERVER (retry); cover: NOT_A_HOLDER, NO_COVER, COVER_CLOSED.
A mined transaction can still revert if the market moved past minShares / minOut:
nothing is spent but gas; rebuild and retry.

## Earn fees: public jobs
GET /v1/jobs lists every order and scheduled buy executable right now, with
calldata and the fee it pays whoever sends it. Simulate first; if a fill misses
its floor, send armData to start its price auction.

## Tools
- MCP server (Streamable HTTP): https://api.bskts.xyz/mcp · local: npx -y @bskts/mcp
- TypeScript SDK: npm i @bskts/sdk viem (typed, policy limits, dry-run guard).
- Skill for shell agents: https://github.com/Tokenise-au/bskts-sdk/tree/main/skills/bskts

## More
- OpenAPI: /openapi.json · Protocol docs: https://bskts.xyz/docs.md
- Numbers are rounded to 8 significant digits; ?pretty=1 indents JSON.

## Notes for agents
- Wallet /v1/tx/* needs ETH gas. /v1/agent/* needs a host signer and a funded
  bskts account; no ETH.
- Create the agent key in a secure host signer; share only its PUBLIC address.
  GET /v1/agent/session?owner=&key= returns account, balance and live
  permission. If inactive, the owner signs once at approvalUrl. Defaults: 10% of
  USDG (min $10) capped $100/day, 0.5% slippage, 7 days; only the owner changes them.
- POST /v1/agent/trade {owner,key,ticker,side:"buy",amountUsdg,slippageBps?} or
  {owner,key,ticker,side:"sell",shares,slippageBps?} returns an unsigned Action,
  EIP-712 typedData and status. slippageBps 1-1000 (default 50, or the grant's
  max if lower; never widen silently). Min $5 of basket value, or the whole position.
- The host signs typedData and POSTs {action,signature} to relayerUrl
  (https://bskts.xyz/relay/v1/account/execute), which simulates first. 402 +
  requiredFee: re-sign ONLY fee (same nonce/deadline) within a host fee cap. After
  an ambiguous timeout check nonceUsed(account,nonce) before building anew.
- SDK: agentSession/buildAgentTrade/executeSession; executeSessionOnce journals
  the plan before signing (fileSessionStore from @bskts/sdk/node; keep journals,
  never keys or signatures). MCP bskts_agent_* tools stay unsigned. Never send a
  private key to the API, MCP or dashboard.
- Daily turnover: buys + sells, per key, resets 00:00 UTC, excludes network fees
  ($10 max per action). Revoking blocks new requests; open orders, schedules and
  Cover listings stay until cancelled in Portfolio / My cover.
- To automate, arm a spend permit (scoped total/period cap, revocable). Anyone may
  execute a due schedule for the fee (GET /v1/jobs); funds never leave the user's
  wallet until an approved rule fires.

## Weekend Cover (agents buy; underwriting is the owner's)
Cover pays a held basket's weekend drop between fromBps and toBps per $ of cover
(300-1000: pays from -3%, in full at -10%). Holders only; the owner turns Cover
on once (session coverEnabled). Prices are $ per $1,000 of cover, +1% fee.
- GET /v1/cover/weeks: weeks a session may buy (this + next 2), terms, limits.
- GET /v1/cover/book/{ticker}?coverUsd=&fromBps=&toBps=&week=&account=: offers by
  range, history (fairPer1k = average payout a weekend) and a quote.
- POST /v1/agent/cover {owner,key,action:"take",ticker,fromBps,toBps,coverUsd,
  week?,maxPricePer1k?,allowPartial?} | {...,action:"request",pricePer1k} |
  {owner,key,action:"cancel",listingId}: unsigned CoverAction + typedData (domain
  "bskts cover"); POST signed to its relayerUrl, same 402 rule (cancels free).
  Premium + fee <= 3% of cover; cover's daily limit is counted apart from trades.
- GET /v1/cover/positions/{account}: cover held, payouts (automatic), listings.
