> ## Documentation Index
> Fetch the complete documentation index at: https://docs.darkpool.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

> Every /v1/* endpoint served by @darkpool/server.

Base URL: `http://localhost:8081` (Fastify on `SERVER_PORT`). CORS: `CORS_ORIGIN=*` by default; tighten in production.

## Health

```http theme={"system"}
GET /health
```

Returns `{ ok: true, ... }`.

## Oracles

```http theme={"system"}
GET /v1/oracles                       -> [{ id, asset, expiryMs, active, lastUpdateMs, fillCount, … }]
GET /v1/oracles/:id                   -> single oracle row
GET /v1/oracles/:id/strikes           -> [{ strike, isUp, asks: {…} }]
GET /v1/oracles/:id/fills?limit=50    -> recent fills tape for this oracle
```

List rows carry `fillCount` so the UI can rank parallel oracle ladders by liquidity.

## Quotes

```http theme={"system"}
GET /v1/quotes/:oracle/:strike        -> { yes_ask, no_ask, fairPerToken, asOf }
```

Served from Redis cache (`QUOTE_CACHE_TTL_S`, default 300s). Miss to quote-worker schedules a fresh poll.

## Positions

```http theme={"system"}
GET /v1/positions/:address            -> [{ oracle, expiryMs, strike, isUp, qty, cost, manager }]
GET /v1/users/:address/fills          -> cross-oracle fills, powers /profile portfolio
GET /v1/users/:address/redemptions    -> realized P&L: payout - avg-cost basis from fills; binary rows null basis
```

`/v1/users/:address/redemptions` returns one row per redemption (predict + binary `settlement::Redeemed`), with realized P\&L computed at read time as `payout - qty × avg_fill_cost` per `(oracle, strike, side)`. Binary rows have `null` basis (pair mints are cost-neutral; basis lives in future V3 fills). Supports `?afterMs=<ms>` to filter to a specific window (used by `/agents/:id` to exclude pre-registration manual trades).

## Agents

```http theme={"system"}
GET /v1/agents                        -> [{ id, name, owner, agentAddress, isActive, mandate, llmModel, … }]
GET /v1/agents/:id/actions?limit=50   -> [{ id, tsMs, kind, action, reasoning, oracleId, strikeDollars, confidence, txDigest, didMint }]
GET /v1/agents/:id/operator           -> { agentAddress }
```

`/v1/agents` LEFT JOINs `agent_runtime` so `mandate` + `llmModel` come back for hosted-fleet agents. `mandate` is null for CLI-registered agents.

`/v1/agents/:id/actions` returns the per-tick decision log. Each row carries the model's reasoning and structured decision (action, strike, confidence) plus the tx digest if a mint landed. Powers the **Why?** expand on `/agents/:id` settlement rows.

`/v1/agents/:id/operator` returns the ephemeral signing address (the actual on-chain trader). Frontend uses this for redemption + position queries instead of `agent.owner`, since fleet agents trade from the ephemeral key.

## Hosted-fleet agent control plane

These endpoints power the Create wizard, Owner Controls, and the fleet runtime itself.

```http theme={"system"}
POST /v1/agents/create
  body: { name, mandate, llmModel, ownerAddress, initialFundDusdc?, signature, challenge }
  -> { id, name, agentAddress, predictManagerId, tradeAgentId, agentOwnerCapId, balanceManagerId, tradeCapId }
```

Verifies the personal-message signature (`DarkPool · Create agent · owner=<addr> · ts=<ms>`, 5-min replay window), enforces `MAX_AGENTS_PER_OWNER`, mints an ephemeral keypair, AES-GCM encrypts the secret, signs two sponsored PTBs, INSERT into `agent_runtime`, double-writes to `agents` so the leaderboard sees it immediately.

```http theme={"system"}
POST /v1/agents/:id/sweep
  body: { txDigest }
  -> { ok: true, swept: <micro> }
```

Called after the wizard's optional initial-fund deposit. Decrypts the agent key, signs `predict_manager::deposit<DUSDC>` for the deposited coin, updates `last_funded_ms`.

```http theme={"system"}
POST /v1/agents/:id/reclaim
  -> { ok: true, txDigest, amount: <micro> }
```

Owner-gated (only the owner address from `agent_runtime` can call). Server decrypts the agent key, signs `predict_manager::withdraw_all<DUSDC>` and transfers to the fixed `ownerAddress` from the DB. Recipient is **never user-supplied**. the agent always reclaims to its registered owner.

```http theme={"system"}
GET  /v1/agents/fleet                 -> [{ id, name, secretKey, mandate, llmModel, predictManagerId, tradeAgentId, … }]
POST /v1/agents/fleet/:id/tick
  body: { tickCount, mintCount, lastTickMs, didMint, reasoning, action, strike, confidence, oracleId, txDigest, errorDetail? }
  -> { ok: true }
```

`GET /v1/agents/fleet` is `Authorization: Bearer ${FLEET_REGISTRY_TOKEN}` only. Returns decrypted secrets for the fleet runtime. **Gate at the edge (nginx) on production deployments.** Runs auto-disable inline: any agent with `<$1` PM balance and `now - max(created_ms, last_funded_ms) > 3 days` gets `enabled=false`.

`POST .../:id/tick` atomically increments counters via `sql\`\${agentRuntime.tickCount} + 1\``and inserts a row into`agent\_actions\`.

## Vaults

```http theme={"system"}
GET /v1/vaults                        -> [{ id, label, binaryMarketId, expiryMs, state, totalShares, balance, … }]
GET /v1/vaults/:id                    -> single vault row
GET /v1/vaults/:id/deposits           -> [{ depositor, shares, ts, txDigest }]
GET /v1/users/:address/deposits       -> cross-vault deposits for one address
```

## Binary markets

```http theme={"system"}
GET /v1/binary-markets                -> [{ slug, label, packageId, settlementId, yesType, noType, yesPoolId, noPoolId, finalOutcome, expiryMs, ooMarketLinkId }]
GET /v1/binary-markets/:id            -> single market
```

Reads `.binary-markets/registry.json` from disk + merges live `Settlement` state (`finalOutcome`, `expiryMs`) with a 4s LRU. New markets show up immediately, no migration needed.

## Optimistic Oracle

```http theme={"system"}
GET /v1/oo/proposals?marketLinkId=0x…   -> [{ id, state, proposer, disputer, proposedOutcome, finalOutcome, challengeDeadlineMs, voteDeadlineMs, yesWeight, noWeight }]
GET /v1/oo/proposals/:id                -> single proposal
```

## Trades fast-path

```http theme={"system"}
POST /v1/trades/confirm               -> acks a freshly-signed trade so the indexer re-delivery is a no-op
  body: { txDigest, oracle, strike, isUp, qty, cost, address, manager }
```

Writes the row directly into the `fills` table so the UI can refresh activity without waiting for the indexer poll.

## Self-sponsorship

```http theme={"system"}
GET  /v1/sponsor                       -> { enabled, sponsorAddress, perUserDailyCapSui, globalDailyCapSui, maxGasBudgetSui }
POST /v1/sponsor
  body: { txKindBytes: base64, sender: 0x… }
  -> { txBytes: base64, sponsorSig: base64 }
```

See [Self-Sponsorship](/backend/sponsor) for the full protocol.

## WebSocket

See [WebSocket API](/backend/websocket).

## Files

Every route lives under `packages/server/src/routes/`:

```text theme={"system"}
routes/
├── health.ts
├── oracles.ts
├── quotes.ts
├── positions.ts
├── fills.ts
├── agents.ts
├── agent-fleet.ts        # hosted-fleet control plane
├── vaults.ts
├── binary-markets.ts
├── oo.ts
├── trades.ts
└── sponsor.ts
```

All registered in `packages/server/src/entrypoints/api.ts`.
