API

Every endpoint, its shape, and its build status stated honestly. Read-only JSON, no key.

Read-only JSON. No key. Base /api/v1.

Every route calls getReadings() in src/lib/readings.ts, the same assembler the landing page uses, so the page and the API cannot disagree about a number.

Responses carry Cache-Control: public, s-maxage=60, stale-while-revalidate=120, except /sessions and /status which are no-store because they describe now.


Readings

GET /v1/basis

Every tracked token.

QueryEffect
symbols=NVDA,SPYrestrict the set
featured=1the landing-page subset
{
  "readings": [ /* Reading[] */ ],
  "session": "regular",
  "asOf": "2026-09-03T05:40:26.900Z",
  "health": [ { "name": "rhj/assets", "ok": true, "ms": 10 } ],
  "allStale": false
}

GET /v1/basis/{symbol}

One reading in full. 400 unknown_symbol if it is not a canonical Stock Token — a matching ticker at a different address is not the same asset.

{
  "symbol": "NVDA",
  "name": "NVIDIA",
  "address": "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC",
  "asOf": "2026-09-03T05:39:08.515Z",

  "reference": {
    "price": 225.37,          // mid x multiplier — use this one
    "mid": 225.37,            // raw underlying mid; never compare a pool to this
    "bid": 225.33, "ask": 225.41,
    "multiplier": 1,
    "halted": false,
    "ageSeconds": 34,
    "source": "rhj"           // "fallback" when degraded
  },

  "pool": {
    "price": 225.42,
    "quote": "USDG", "dex": "uniswap", "version": "v3",
    "liquidityUsd": 6064982,  // TVL, not executable depth
    "pairId": "0x…"
  },

  "poolCount": 29,
  "basisBps": 2,              // deepest pool vs reference
  "spreadBps": 571,           // cheapest pool vs dearest
  "flag": "TIGHT",
  "session": "overnight",
  "stale": false
}

GET /v1/pools/{symbol}

Every pool holding the token as base, cheapest first, above the $3,000 dust threshold. Carries a note restating that liquidityUsd is TVL rather than executable depth.

GET /v1/board

All tokens ranked by pool-to-pool spread — the working list, where the same asset wears two price tags at the same second.

QueryEffect
min_spread_bps=400filter to the flagged band

Context

GET /v1/sessions

{
  "state": "overnight",
  "label": "Overnight session · reference thin",
  "easternTime": "01:39:07",
  "nextTransition": "2026-09-03T08:00:00.908Z",
  "referenceFrozen": false,
  "frozenSince": null
}

When state is closed, frozenSince is the last weekday 20:00 ET and referenceFrozen is true. Every reading depends on this: a 4% gap in a regular session and a 4% gap at 03:00 on a Sunday are not the same event.

GET /v1/status

Upstream health, per-source timings, and how many rows in the current response are live versus stale — with the reason for each stale row.

{
  "ok": true,
  "assembledInMs": 37,
  "sources": [ { "name": "rhj/assets", "ok": true, "ms": 1 } ],
  "rows": { "total": 12, "live": 12, "stale": 0 },
  "staleSymbols": [],
  "fallbackCapture": "2026-09-02T18:33:00.000Z"
}

A feed claiming perfect uptime is a feed you should not read. This one reports its failures.


Treasury

GET /v1/treasury

The treasury's balance from chain, what the published mandate would do with the current readings, and the ledger of what it has seen. ?tail=N (default 120, max 1440) sets how many minutes of ledger come back.

{
  "asOf": "2026-09-12T23:48:11.402Z",
  "session": "closed",
  "balance": { "configured": false, "reason": "No treasury wallet is configured on this deployment. …" },
  "armThresholdUsd": 50000,
  "simulated": {
    "label": "SIMULATED — signals at quoted prices under the published mandate. No capital deployed, no fill, not executable size.",
    "notionalUsd": 50000,
    "notionalIsReal": false,
    "feeAssumptionBpsPerLeg": 30,
    "now": [ { "symbol": "MSTR", "signal": { "kind": "pool-to-pool", "spreadBps": 108.4, "sizeUsd": 208, "netUsd": 1.01, "buy": { … }, "sell": { … } }, "reason": null },
             { "symbol": "NVDA", "signal": null, "reason": "Spread 12 bps does not clear the assumed 60 bps round trip." } ],
    "recordedThisMinute": true
  },
  "ledger": { "entries": 1, "since": "2026-09-12T23:48", "storage": "netlify", "tail": [ … ] }
}

When a wallet is configured, balance carries address, usdg, eth, usd, block, readAt and an explorer link — every field a reader needs to confirm the figure themselves. Every call records the current minute into the ledger, idempotently; storage says whether that record is durable (netlify) or local memory. Cache-Control: no-store.

Nothing under simulated is a fill. The response says so in its own shape.

Errors

CodeMeaning
400 unknown_symbolNot a canonical Stock Token. Check /v1/basis for the tracked list.
503 no_readingNo reading could be assembled, and the fallback did not cover it.
503 no_poolsNo pools above the dust threshold.

Degradation is not an error. If an upstream fails, the response is still 200 with affected rows marked stale: true, flag: "DARK" and a degraded reason. Returning a plausible-looking number without saying where it came from would defeat the point of the product.


Build status

EndpointStatus
/v1/basis, /v1/basis/{symbol}, /v1/pools/{symbol}, /v1/boardlive
/v1/sessions, /v1/statuslive
/v1/history/{symbol}not built — needs a store; see docs/roadmap.md
/v1/quote/{symbol}?usd=uncertain — see docs/roadmap.md
/v1/alertsnot built

Nothing above is described as shipping until it does.