Public API

Base URL: https://<api host>

The API serves public data to anyone and builds transactions for signed-in wallets. Responses are JSON with a data field and a meta field (requestId, asOf, slot, mode). Amounts are decimal integer strings: USDC has 6 decimals, shares 9, weights and fees are in basis points.

Public reads

MethodPathReturns
GET/v1/platformTotal AUM, platform cap, live strategies, epochs verified, commitment chain head
GET/v1/deploymentCluster, genesis hash, program IDs, USDC mint, the executor's key and its current attestation
GET/v1/protocolThe protocol fee account, the $UNSEEN mint, fees awaiting the next buyback, total USDC spent and $UNSEEN burned, the last buyback's signature
GET/v1/strategiesStrategies with terms, status and attested stats
GET/v1/strategies/:strategyIdOne strategy
GET/v1/strategies/:strategyId/statsHourly attested return, drawdown and exposure band
GET/v1/strategies/:strategyId/epochsEpochs with NAV and state
GET/v1/strategies/:strategyId/revealed-tradesRevealed trades with their openings
GET/v1/revealed-executions/:executionIdOne revealed trade
GET/v1/revealed-strategies/:strategyId/navNAV rebuilt from revealed data
GET/v1/executionsEvery execution, unattributed: market, side, notional, slot, exec account, commitment
GET/v1/guardian/outcomesPauses, challenge resolutions and voided epochs
GET/v1/eventsServer-sent events: execution.observed, platform.updated, heartbeats

Lists take limit and cursor; the response carries the next cursor.

curl https://<api host>/v1/executions?limit=5

Sign in

POST /v1/auth/challenge   { "wallet": "<address>" }
POST /v1/auth/verify      { "wallet": "<address>", "challengeId": "…", "signature": "<base64>" }

Sign the challenge's message (UTF-8) with the wallet; the signature is the raw 64-byte Ed25519 signature in base64. verify returns an accessToken; send it as Authorization: Bearer <token>.

Your account

MethodPath
GET/v1/meYour wallet and roles
GET/v1/me/portfolioHoldings, pending requests, claimable amounts
GET/v1/me/eligibility/:strategyIdWhether you can deposit, and what is missing

Transactions

Each action returns a transaction plan: unsigned Solana transaction bytes (base64), its blockhash and expiry. Sign the exact bytes with the wallet, submit them, and poll the plan until it is confirmed.

MethodPathBody
POST/v1/transactions/deposit{ strategyId, amountUsdc }
POST/v1/transactions/withdrawal{ strategyId, shares }
POST/v1/transactions/claim-withdrawal{ strategyId }
POST/v1/transactions/cancel-request{ strategyId }
POST/v1/transactions/create-strategyleaders: name, kind, stake, signal key, fee, delay, markets, leverage, drawdown stop
POST/v1/transactions/signal-keyleaders: { strategyId, signalKey, action: "register" | "rotate" }
POST/v1/transactions/claim-leader-feeleaders: { strategyId }
POST/v1/transactions/:id/submit{ signedTransaction }
GET/v1/transactions/:idThe plan and its state

Every write takes an Idempotency-Key header. Keep the key when you retry the same request; a new request needs a new key.

Signals

MethodPathBody
POST/v1/signals/sealedleaders: a sealed envelope { version, ephemeralPublicKey, nonce, ciphertext } from the SDK; returns the executor's signed admission receipt
POST/v1/leader/strategies/:strategyId/consoleleaders: a one-minute proof signed by the leader's wallet; returns the private console

The API relays the envelope to the executor without being able to read it. Build envelopes with the SDK.

Applications

MethodPathBody
POST/v1/leader-applications{ name, leaderKind, markets, bookSize, wallet?, contact }
POST/v1/follower-access-requests{ wallet, contact?, notUSPerson: true, notInEU: true }

Errors

Errors carry { "error": { "code", "message", "retryable", "details" } }. details maps field names to messages for form errors. Retry only when retryable is true, with the same idempotency key.

The contract package

@unseen/contract types every route and response, and includes the client, commitment verification and the leader-console proof helper:

import { createUnseenClient } from '@unseen/contract'

const api = createUnseenClient({ baseUrl: 'https://<api host>' })
const { data } = await api.request('strategies', { query: { limit: '20' } })