Skip to main content

Explore & loan data auth

Explore (CLOB analytics) and loan-data routes use a dedicated Explore gate on the API. This is separate from the public quote create flow and from maker org API keys.

Formula:

(SIWE ∧ Turnstile + allowed Origin) ∨ taker-delegate JWT (API-key mint only)

Plus an :explore rate limit on IP, wallet, and taker API key.

For SIWE login itself, see Authentication (SIWE). For taker delegate mint used by wallet backends, see Add to your crypto wallet (API taker).

Who uses which path​

AudienceCredentialTurnstileOrigin
Trading app (/explore, /loan-explorer)SIWE bearer from wallet sign-inYes when enforced (explore-clob / explore-loan)Browser sends allowed Origin (Vite dev proxy injects it for same-origin GETs)
Integrator backendShort-lived taker-delegate JWT minted with a taker API keySkippedNot required
Maker API keysRejected on explore / loan-data routes
Long-lived taker API keysNot sent directly. Mint a delegate first
SIWE-minted delegates (taker admin UI)Work for quote create only; rejected on explore → 401 taker API key delegate required

Website path (trading app)​

After the user signs in with Ethereum, the app sends:

Authorization: Bearer <siwe-session-token>
Origin: <allowed CORS origin>
CF-Turnstile-Response: <token> # when Turnstile is enforced

Turnstile public config comes from GET /api/landing-page-settings (turnstile.sitekey, turnstile.enabled). The shared client (@opshuns/shared) attaches Turnstile automatically on explore paths when enforcement is on.

Widget action must match the route family:

RoutesTurnstile action
/api/explore/clobs/*, /api/clob/*explore-clob
/api/explore/loans/*, /api/margin/loans*explore-loan

When TURNSTILE_SECRET is unset on the API, Turnstile fails open; Origin is still required on the website path.

Trading app pages​

PathGateData loaded after sign-in
/exploreSIWE + Explore gateCLOB book, activity, fills, summary
/loan-explorerSIWE + Explore gateLoan summary, at-risk, recent openings, activity chart, live stream
/trade (advanced terminal)SIWE (UI only)RFQ quote box; quote create itself remains public on the API

CLOB triplet discovery still uses public GET /api/indexer/cores plus optional on-chain mothership logs before authenticated book calls.

Integrator path (taker delegate)​

Do not send a long-lived taker API key on explore requests. Mint a short-lived delegate per end-user wallet:

POST /api/taker/delegate
Authorization: Bearer <taker-api-key-secret>
Content-Type: application/json

{ "address": "0xUserWallet", "expires_in": 300 }

Use the returned JWT as Authorization: Bearer <delegate-jwt> on explore HTTP routes. Delegates minted this way stamp key_id in the JWT (required for explore). SIWE-minted delegates from the taker admin console omit key_id and are rejected here.

Example:

GET /api/clob/0x…/book?chain_id=31337
Authorization: Bearer <taker-delegate-jwt>

No Turnstile or Origin header is required on this path.

Protected routes​

MethodPathTurnstile action
GET/api/explore/clobs/:addressexplore-clob
POST/api/explore/clobs/:address/streamexplore-clob
GET/api/clob/bookexplore-clob
GET/api/clob/:address/bookexplore-clob
GET/api/clob/:address/activityexplore-clob
GET/api/clob/:address/fillsexplore-clob
GET/api/clob/:address/summaryexplore-clob
GET/api/explore/loans/summaryexplore-loan
GET/api/explore/loans/at-riskexplore-loan
GET/api/explore/loans/recentexplore-loan
GET/api/explore/loans/activityexplore-loan
GET/api/explore/loans/eventsexplore-loan
POST/api/explore/loans/streamexplore-loan
GET/api/margin/loansexplore-loan
GET/api/margin/loans/:idexplore-loan
GET/api/margin/loans/:id/collateralexplore-loan

WebSockets​

Stream tokens are minted over HTTP under the Explore gate, then joined anonymously on /socket:

TopicMintPush events
explore_clob_stream:<token>POST /api/explore/clobs/:address/streamexplore_clob_snapshot, explore_clob_event, explore_clob_transfer
explore_loan_stream:<token>POST /api/explore/loans/streamexplore_loan_snapshot, explore_loan_event

Socket join only validates the stream token: not SIWE or Turnstile.

API pipeline​

:api → :authenticated → :explore_access
RequireExploreAuth # :siwe | :taker_delegate with key_id
Geoblock (:quote)
RateLimit (:explore. IP + wallet + taker key)
ExploreAccess # taker_delegate OR (Origin + Turnstile)

Errors​

HTTPerrorTypical cause
401siwe or taker delegate requiredMissing / invalid bearer, or maker/taker long-lived key used as Bearer
401taker API key delegate requiredSIWE-minted delegate on an explore route
403origin header requiredWebsite path without Origin (non-browser client)
403origin not allowedOrigin not on CORS allowlist
403turnstile verification requiredTurnstile enforced but token missing / invalid
429too many requestsExplore rate limit (IP, wallet, or taker key)

Local development​

  1. API: include trading origins in CORS_ORIGINS, e.g. http://localhost:3000,http://localhost:3005.
  2. Web: leave VITE_API_URL empty so Vite proxies /api and /socket (the proxy adds Origin for same-origin GETs).
  3. Turnstile: optional locally (VITE_TURNSTILE=false); the API fails open without TURNSTILE_SECRET.