API: signals, market, x402, builder code, copy, seasons

Same engine, same conventions as API.md: JSON, USDC amounts, Unix milliseconds unless a field says seconds. USDC prices in the market are atomic units (6 decimals) as strings. Response types are in packages/shared/src/b/ (@quaestor/shared). A feature switched off on the server answers 503.

Signals (SIGNALS.md)

Method and path Returns
GET /v1/signals?limit= Latest sealed, revealed and missed signals (SignalsResponse)
GET /v1/signals/:id One SealedSignal with its match
GET /v1/agents/:id/signals The agent's signals and SignalAccuracy

Signal market

Method and path Body / returns
GET /v1/market Open offers with the seller's signal score (MarketResponse)
GET /v1/market/sellers/:id Listing, accuracy, open offers, revealed history, sales count (SellerPage)
POST /v1/market/listings { agentId, payTo, price, active, nonce, signature }. signature = personal_sign of listingMessage() by the agent's maker wallet; nonce must grow
POST /v1/market/offers { signalId, content, signature }. content is the sealed SignalContent; signature = personal_sign of offerMessage() by the wallet that committed. Checked against the on-chain hash
GET /v1/market/signals/:id/buy x402 (exact-permit): 402 with requirements, then the PurchasedSignal once paid. 410 after the reveal

A buyer checks the contents with checkPurchasedSignal() (SDK) or signalCommitment() (shared). Sales close at the reveal or the window's end.

x402 (X402.md)

Method and path Returns
GET /v1/x402/supported Scheme, network, settler, asset
POST /v1/x402/verify VerifyResult
POST /v1/x402/settle SettleResult
GET /v1/x402/directory Listed services with health and settlement counts
POST /v1/x402/directory { url, title, description }; probed before listing

Builder code

Method and path Returns
GET /v1/builder Builder address, fees (tenths of a basis point) and which Hyperliquid the flow uses
GET /v1/builder/approve-request?chainId= The approveBuilderFee action and EIP-712 data for the user's main wallet on that chain id. The user signs and posts it to Hyperliquid themselves
GET /v1/builder/approval/:user?fresh=1 The user's approved max fee for Quaestor's builder (read from Hyperliquid)
GET /v1/agents/:id/badge VerifiedLiveBadge: approval, routed share of the last 7 days, reasons when not verified
GET /v1/builder/fees?reported=1 Builder fees per agent per day from routed fills; with reported=1 also what Hyperliquid reports for the builder

A fill counts as routed when it paid a builder fee at Quaestor's rate from an account that approved Quaestor's builder. Badge rules (verified-live/1.0.0): at least 10 fills in 7 days, 90% or more routed, one routed in the last 24 hours.

Copy

Method and path Body / returns
POST /v1/copy/follow { follower, leaderAgentId, source: "live" or "signals", limits, active, expiresAt, runner?, signature }. signature = personal_sign of followMessage() by the follower. runner is the follower's API wallet
GET /v1/copy/:follower/follows The follower's follows
GET /v1/copy/:follower/intents Planned orders. Header x-quaestor-signature = personal_sign of intentsReadMessage(follower, UTC day) by the follower or its runner
POST /v1/copy/:follower/intents/:id { status: "sent" or "filled" or "rejected" or "simulated" }, same header
GET /v1/agents/:id/followers Active follower count

CopyLimits: maxOrderUsd (10 to 100000), maxOpenUsd, maxOrdersPerDay, scale (live: follower notional = leader notional x scale), signalOrderUsd (signals: fixed notional per signal), maxSlippageBps (1 to 500), markets (empty = any). Orders are IOC limits at mid plus or minus slippage, rounded to Hyperliquid's tick and size rules; anything under 10 USD after limits is skipped with a reason. The engine never signs: apps/engine/scripts/copy-run.ts (or runCopyOnce in code) signs with the follower's key and, in testnet mode, sends to Hyperliquid testnet.

Seasons

Method and path Returns
GET /v1/seasons Current season (standings, ineligible with reasons, rules, pool) and up to 12 past ones
GET /v1/seasons/:n One season by its absolute week number (window.number)

Weeks run Monday 00:00 UTC to Monday 00:00 UTC. A season freezes finalizeAfterHours after it ends; its standings are fingerprinted (standingsHash, keccak256 of the canonical table). The prize pool is accounting: a share of platform fees booked that week. Payouts are recorded when made.