{"openapi":"3.1.0","info":{"title":"PRIZM Trading API","version":"1.1.0","description":"Non-custodial Solana trading from your own agent or bot: swaps, limit & DCA orders, SOL-PERP leverage, leveraged SOL/USDC spot on Kamino, Kamino/marginfi/Meteora lending & liquidity, and the VIZION signal reads. Every build endpoint returns a transaction built for the API key's bound wallet with the wallet's signature slot empty (a marginfi or Meteora build may carry the server's signature over a throwaway account keypair it created — never over funds; /lev loops return an ordered list of such legs) — sign it locally (the key can never move funds) and broadcast via /execute; resolve a pending broadcast with /tx/{signature}, never by rebuilding. Fee rates quoted here are the ones this deployment is configured with (swap 0.85%, DeFi 0.33%, perps 0.10%); the per-call fee figure on every quote and build is authoritative. Generate a key for free from the wallet page at https://www.prizm.trading/wallet."},"servers":[{"url":"https://www.prizm.trading/api/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"pzk_<64 hex>","description":"A PRIZM API key, minted free by a wallet holder at https://www.prizm.trading/wallet. Send as `Authorization: Bearer pzk_…` — the word Bearer is case-sensitive and followed by exactly one space. No key, no access. A key is bound to one wallet and can only build transactions for it (which it cannot sign) and read data for it."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"bytes":{"type":"integer","description":"/lev too_large only · the serialized size of the refused transaction."},"ref":{"type":"string","description":"5xx only · the platform's request id for this failure. Quote it when reporting one; the detail is in our log, never in the message."}},"required":["code","message"],"additionalProperties":false}},"additionalProperties":false},"Fee":{"type":"object","properties":{"bps":{"type":"integer","description":"Fee rate in basis points (0 for promoted wallets)."},"feeBase":{"type":"string","description":"The fee amount in base units of the input mint, as a string."},"takenIn":{"type":"string","description":"The mint the fee is taken in."},"at":{"type":"string","enum":["execution","placement"],"description":"How the fee is taken. `execution` (swaps): CARVED OUT of `amountBase` — the swap runs on `amountBase − feeBase`, so the wallet never spends more than it committed. `placement` (limit and DCA orders): ADDED ON TOP of the order's committed amount and transferred when the order is placed — the order escrows the full amount you named and the fee leaves alongside it. A placement fee is not refunded if the order never fills, expires or is cancelled."},"promoted":{"type":"boolean","description":"Present and true when the wallet is fee-exempt. On a promoted wallet the object is exactly { bps: 0, promoted: true } · feeBase, takenIn and at are absent."}},"additionalProperties":true},"SwapBuild":{"type":"object","properties":{"txBase64":{"type":"string","description":"Swap transaction, base64, with the wallet's signature slot empty · deserialize, sign locally, then POST to /execute."},"lastValidBlockHeight":{"type":["integer","null"]},"quote":{"type":"object","properties":{"inAmount":{"type":"string","description":"Base units actually swapped after the fee is carved out."},"outAmount":{"type":"string"},"priceImpactPct":{"type":["string","null"]}},"additionalProperties":true},"fee":{"type":"object","properties":{"bps":{"type":"integer","description":"Fee rate in basis points (0 for promoted wallets)."},"feeBase":{"type":"string","description":"The fee amount in base units of the input mint, as a string."},"takenIn":{"type":"string","description":"The mint the fee is taken in."},"at":{"type":"string","enum":["execution","placement"],"description":"How the fee is taken. `execution` (swaps): CARVED OUT of `amountBase` — the swap runs on `amountBase − feeBase`, so the wallet never spends more than it committed. `placement` (limit and DCA orders): ADDED ON TOP of the order's committed amount and transferred when the order is placed — the order escrows the full amount you named and the fee leaves alongside it. A placement fee is not refunded if the order never fills, expires or is cancelled."},"promoted":{"type":"boolean","description":"Present and true when the wallet is fee-exempt. On a promoted wallet the object is exactly { bps: 0, promoted: true } · feeBase, takenIn and at are absent."}},"additionalProperties":true},"signer":{"type":"string"}},"additionalProperties":true},"PerpBuild":{"type":"object","properties":{"txBase64":{"type":"string","description":"Unsigned perps request transaction, base64."},"lastValidBlockHeight":{"type":["integer","null"]},"position":{"type":"string","description":"The position account PDA."},"positionRequest":{"type":"string","description":"The request account the keeper acts on."},"counter":{"type":"string","description":"Request nonce."},"feeLamports":{"type":"string","description":"Platform fee in lamports riding this transaction · on an OPEN, 0.10% of `sizeUsd` (notional) valued in SOL at the server's own mark, paid to the treasury on top of the collateral; always \"0\" on a close, and \"0\" for promoted wallets. The fee settles when THIS request transaction lands and is NOT returned if the keeper later closes the request and refunds the collateral — a fill that never happens still paid it. Only a transaction that fails pays nothing."},"signer":{"type":"string"},"verify":{"type":"string","description":"A standing instruction, returned on every build: check the request bytes — size, collateral, slippage, fee destination — against the params above before signing, if you do not fully trust this endpoint."}},"additionalProperties":true},"DefiBuild":{"type":"object","properties":{"txBase64":{"type":"string","description":"Lending/liquidity transaction, base64, with the wallet's signature slot (the fee payer, index 0) empty. A Meteora deposit and a marginfi build that creates an account (or wraps SOL through an ephemeral keypair) ALREADY carry the server's signature over that throwaway keypair — never over funds; add the wallet's signature and broadcast."},"lastValidBlockHeight":{"type":"integer","description":"The last block height the transaction's blockhash is valid at. After a pending broadcast, /tx's blockHeight past this means the transaction can never land and a rebuild is safe."},"signer":{"type":"string"},"feeBps":{"type":"integer","description":"Platform fee in bps · 33 on a deposit, CARVED OUT of `amountUi` (the venue receives `amountUi` minus the fee). 0 for promoted wallets; withdraw, borrow and repay carry no fee instruction at all."},"feeUi":{"type":"number","description":"Fee in human units."},"depositUi":{"type":"number","description":"Net amount deposited after the fee (deposits)."},"symbol":{"type":"string"},"decimals":{"type":"integer"},"mintIsNative":{"type":"boolean","description":"True when the asset is native SOL · `ownerTokenAccount` is null and the balance delta to bound is the lamport balance."},"poolAddress":{"type":"string","description":"meteora · the DLMM pool the transaction targets."},"symbolX":{"type":"string","description":"meteora · the pool's X side."},"symbolY":{"type":"string","description":"meteora · the pool's Y side."},"depositMint":{"type":"string","description":"meteora · the mint deposited (deposit), or empty on a withdraw."},"depositSymbol":{"type":"string","description":"meteora · that mint's symbol."},"reserveAddress":{"type":"string","description":"The venue reserve/pool the action targets."},"priority":{"description":"every venue · the compute-unit price this transaction carries, and the market read that set it (floor 30k, cap 1M microLamports/CU).","oneOf":[{"type":"object"},{"type":"null"}]},"ownerTokenAccount":{"type":["string","null"],"description":"The owner's associated token account for this asset, null for native SOL. Lets you bound the token balance delta before signing."},"createdPosition":{"type":["string","null"],"description":"meteora deposit · the position account this transaction creates (the server signs only that throwaway keypair). SAVE IT: pass it back as `positionAddress` to withdraw."},"createdAccount":{"type":["string","null"],"description":"marginfi · a new marginfi account this transaction creates, null when an existing one is reused. When non-null the server has ALREADY SIGNED that throwaway keypair's slot (as it does an ephemeral wSOL account the SDK may add on any marginfi build); the wallet's slot is always empty."}},"additionalProperties":true},"ExecuteResult":{"type":"object","properties":{"signature":{"type":"string","description":"The transaction signature."},"status":{"type":"string","enum":["confirmed","finalized","pending","failed"],"description":"'confirmed' or 'finalized' = landed (slot present). 'failed' = landed and errored (err present). 'pending' = unconfirmed within the 25-second window · poll /tx/{signature}, never rebuild."},"err":{"description":"Present only when status is failed."},"slot":{"type":["integer","null"],"description":"The slot it landed in · present once confirmed or finalized, absent while pending or failed."},"blockHeight":{"type":["integer","null"],"description":"pending only · the confirmed block height when the window closed; compare it with the build's lastValidBlockHeight to know when a rebuild is safe."},"resolveWith":{"type":"string","description":"pending only · the path to poll: /api/v1/tx/{signature}."}},"additionalProperties":true}}},"paths":{"/key":{"get":{"operationId":"getKeyInfo","summary":"Key introspection · call this FIRST. Proves the key is live and returns its bound wallet, scopes and rate ceilings.","x-scope":"none · any valid key","responses":{"200":{"description":"The key's identity","content":{"application/json":{"schema":{"type":"object","properties":{"wallet":{"type":"string","description":"The wallet every transaction is built for."},"scopes":{"type":"array","items":{"type":"string","enum":["read","trade","perps","defi"]}},"label":{"type":"string","description":"The key's user-given name (may be empty)."},"preview":{"type":"string","description":"The key's display preview."},"createdAt":{"type":"integer"},"limits":{"type":"object","description":"Per-key rate ceilings: read/trading, each perMinute and perDay."},"model":{"type":"string","description":"One-line reminder of the build-and-sign model."}},"required":["wallet","scopes"],"additionalProperties":false}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/markets":{"get":{"operationId":"listMarkets","summary":"The curated token catalog · symbols, mints and decimals. Swaps and orders accept any mint pair; read this for decimals and symbols.","x-scope":"read","responses":{"200":{"description":"Tokens · at most 150, the platform's curated catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"type":"array","items":{"type":"object","properties":{"mint":{"type":"string"},"symbol":{"type":"string"},"name":{"type":"string"},"decimals":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":false}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/prices":{"get":{"operationId":"getPrices","summary":"Spot USD prices for up to 50 mints.","x-scope":"read","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string","description":"Comma-separated mint addresses."},"description":"Mint addresses, never symbols.","example":"So11111111111111111111111111111111111111112,EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"}],"responses":{"200":{"description":"Prices · a mint with no price is simply absent from the map.","content":{"application/json":{"schema":{"type":"object","properties":{"prices":{"type":"object"}},"additionalProperties":false}}}},"400":{"description":"bad_ids · `ids` is empty, or an entry is not a Solana mint address (the message names it; symbols are never accepted). too_many_ids · more than 50 entries in one call (counted before de-duplication). Nothing is silently trimmed or filtered."},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"unconfigured · this deployment has no price backbone configured. Never answered as an empty map. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/balances":{"get":{"operationId":"getBalances","summary":"The wallet's holdings · SOL plus every non-zero token balance. Size orders from this.","x-scope":"read","responses":{"200":{"description":"Holdings","content":{"application/json":{"schema":{"type":"object","properties":{"wallet":{"type":"string"},"sol":{"type":"object","description":"{ lamports: string, ui: number }"},"tokens":{"type":"array","description":"[{ mint, symbol, name, decimals, amountBase, amountUi }] · largest amountUi first · symbol and name are null for a mint the catalog does not know. Served from an 8-second per-wallet snapshot.","items":{"type":"object","properties":{"mint":{"type":"string"},"symbol":{"type":["string","null"]},"name":{"type":["string","null"]},"decimals":{"type":"integer"},"amountBase":{"type":"string"},"amountUi":{"type":"number"}},"additionalProperties":true}}},"required":["wallet","sol","tokens"],"additionalProperties":false}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/defi/pools":{"get":{"operationId":"listDefiPools","summary":"Curated Meteora DLMM pools (address, pair, TVL, APR) · where poolAddress for /defi/build comes from.","x-scope":"read","responses":{"200":{"description":"Pools · six curated pools, deepest TVL first; a pool whose metadata could not be fetched is omitted rather than served blank. Cached 120 s.","content":{"application/json":{"schema":{"type":"object","properties":{"updatedAt":{"type":"integer"},"pools":{"type":"array","items":{"type":"object","properties":{"poolAddress":{"type":"string"},"name":{"type":"string"},"mintX":{"type":"string"},"mintY":{"type":"string"},"symbolX":{"type":"string"},"symbolY":{"type":"string"},"decimalsX":{"type":"integer"},"decimalsY":{"type":"integer"},"tvlUsd":{"type":"number"},"apr":{"type":"number","description":"Fee APR in % · Meteora's daily fee/TVL figure × 365 (a simple annualisation, not compounded; Meteora's compounded `apy` only when the daily figure is absent)."},"price":{"type":"number"}},"additionalProperties":true}}},"additionalProperties":false}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/positions":{"get":{"operationId":"getPositions","summary":"The wallet's live SOL-PERP positions.","description":"Read straight off the wallet's derived position accounts, so an open position appears here whether it was opened through PRIZM or anywhere else · with one limit: a long is read against the SOL collateral custody and a short against the USDC one, which is what this API builds; a short opened elsewhere against USDT collateral has a different account and is not scanned. An empty array means flat: a side with no account, or one whose size is zero, is omitted rather than served as a zero row.","x-scope":"read","responses":{"200":{"description":"Positions","content":{"application/json":{"schema":{"type":"object","properties":{"perps":{"type":"array","items":{"type":"object","properties":{"market":{"type":"string","enum":["SOL-PERP"]},"side":{"type":"string","enum":["long","short"],"description":"A long is funded from the SOL custody, a short from the stable one · both are opened and closed with SOL."},"position":{"type":"string","description":"The position account address · the same PDA /perps/build acts on."},"entry":{"type":"number","description":"Average entry price in USD."},"sizeUsd":{"type":"number","description":"Position notional in USD."},"collateralUsd":{"type":"number","description":"Collateral escrowed against it, in USD, as the program holds it."},"leverage":{"type":"number","description":"sizeUsd / collateralUsd · 0 when there is no collateral to divide by."},"liqPriceEst":{"type":"number","description":"ESTIMATED liquidation price from entry, leverage and the venue's maintenance margin plus close-side fees, WITH the borrow accrued so far inside it whenever `borrowFeeUsd` is a number — the same line the terminal draws. When `borrowFeeUsd` is null the borrow could not be read and this number is optimistic by exactly that much, and it drifts further the longer the position lives. The venue settles at its own oracle, which can sit a breath from any mark: stay clear of it, and do not treat it as the venue's number. `0` on a long at or under 1x means no price above zero liquidates it."},"borrowFeeUsd":{"type":["number","null"],"description":"Borrow accrued against the position since its interest snapshot, in USD · the one cost that grows while the position is held, already inside `liqPriceEst`. Null when the custody's rate could not be read — unknown, never zero."}},"additionalProperties":true}}},"additionalProperties":false}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/health":{"servers":[{"url":"https://www.prizm.trading/api"}],"get":{"operationId":"getHealth","summary":"Unauthenticated liveness pulse · booleans, latencies and ages only.","description":"For a synthetic monitor. Probes the store (PING), the paid RPC (getHealth) and the router (a token search) once a minute per instance and serves the cached verdict; `crons` carries, per scheduled job, the seconds since it last completed and whether that is inside its schedule's threshold. Never a value, a host or a variable name. 30 requests a minute per IP, `cache-control: no-store`.","x-scope":"none · no key is read; a bearer header is ignored","responses":{"200":{"description":"The site is up. `jupiter.ok: false` means routing is impaired while the store and the RPC answer.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when the store and the RPC both answered."},"kv":{"type":"object","properties":{"ok":{"type":"boolean"},"latencyMs":{"type":["integer","null"]}},"additionalProperties":true},"rpc":{"type":"object","properties":{"ok":{"type":"boolean"},"latencyMs":{"type":["integer","null"]}},"additionalProperties":true},"jupiter":{"type":"object","properties":{"ok":{"type":"boolean"},"latencyMs":{"type":["integer","null"]}},"additionalProperties":true},"crons":{"type":"object","additionalProperties":{"type":"object","properties":{"ageSec":{"type":["integer","null"],"description":"Seconds since the job last completed · null when it has never stamped."},"ok":{"type":"boolean"}},"additionalProperties":true},"description":"Keyed by job name (notify, liq-map, ops-deadman, vizion-match, vault-migrate)."}},"additionalProperties":true}}}},"401":{"description":"Never · this endpoint takes no key."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"The store or the RPC is down · the same body, with `ok: false`. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/defi/rates":{"get":{"operationId":"getDefiRates","summary":"Lending rates per asset on Kamino and marginfi, plus jitoSOL stake APY, served from a 60-second shared cache (`updatedAt` is the read time). What a carry bot allocates from · note the units differ per venue (see each field), and marginfi serves a supply rate only.","x-scope":"read","responses":{"200":{"description":"Rates","content":{"application/json":{"schema":{"type":"object","properties":{"updatedAt":{"type":"integer"},"jitoStakeApy":{"type":["number","null"],"description":"% · the LST's own yield, accrued in its exchange rate, as published by Sanctum. Null when the LST's rate could not be read · unknown, never zero."},"partial":{"type":"boolean","description":"True when a venue's array below is empty because it could not be READ, not because it lists nothing. Do not reallocate away from an unreachable venue on the strength of an empty array."},"unreachable":{"type":"array","items":{"type":"string"},"description":"Which venues could not be read this call (\"kamino\", \"marginfi\"). Empty when both answered."},"kamino":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"mint":{"type":"string"},"decimals":{"type":"integer"},"supplyApy":{"type":"number","description":"% · a true APY; klend compounds it."},"borrowApy":{"type":"number"},"tvlUsd":{"type":"number"}},"additionalProperties":true}},"marginfi":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"mint":{"type":"string"},"decimals":{"type":"integer"},"supplyApy":{"type":"number","description":"% · an APR, NOT an APY: marginfi's lendingRate is base rate x utilisation with no compounding applied."},"tvlUsd":{"type":"number"}},"additionalProperties":true}},"fees":{"type":"object","properties":{"lendingBps":{"type":"integer","description":"The deposit fee a carry bot nets its rates against · read from the constant the builders charge (33)."},"swapBps":{"type":"integer","description":"The swap fee (85) · for the swap legs of a loop."}},"additionalProperties":true}},"additionalProperties":true}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_rates · both lending venues are unreachable right now. One venue missing is served with that venue's array empty and named in `unreachable`, with `partial` true. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/defi/positions":{"get":{"operationId":"getDefiPositions","summary":"The wallet's live lending positions on Kamino and marginfi · supplied AND borrowed, in human units, each line valued by the venue's own oracle.","description":"Read fresh on every call. `kamino` and `marginfi` are the SUPPLIES; `kaminoBorrowed` and `marginfiBorrowed` are the DEBTS against them, each with its borrow APY. `usdValue` is the venue's own oracle valuation of the line and is null when that oracle could not be read — unknown, never zero. `partial` is true when one venue could not be reached and its arrays are empty for that reason; when neither can be reached the answer is 503 `no_positions`, not an empty wallet. Every marginfi row, supply or debt, carries `account` — WHICH marginfi account holds the line, since a wallet may own several — and a withdraw built through POST /defi/build should pass it back as `account`, or a max withdraw can be built against another account's balance and refused before signing.","x-scope":"read","responses":{"200":{"description":"Positions","content":{"application/json":{"schema":{"type":"object","properties":{"wallet":{"type":"string"},"kamino":{"type":"array","description":"Kamino supplies (main market only).","items":{"type":"object","properties":{"reserveAddress":{"type":"string"},"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"supplyApy":{"type":"number","description":"% per year"},"usdValue":{"type":["number","null"]},"ltv":{"type":["number","null"],"description":"the reserve's loan-to-value · how much may be borrowed against this line"},"liqThreshold":{"type":["number","null"],"description":"the LTV at which the line is liquidated"}},"additionalProperties":true}},"marginfi":{"type":"array","description":"marginfi supplies.","items":{"type":"object","properties":{"reserveAddress":{"type":"string"},"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"supplyApy":{"type":"number","description":"% per year"},"usdValue":{"type":["number","null"]},"account":{"type":"string","description":"The marginfi account holding this line · pass it back as `account` on a POST /defi/build withdraw so the transaction is built against this account's balance."}},"additionalProperties":true}},"kaminoBorrowed":{"type":"array","description":"Kamino debts (main market only).","items":{"type":"object","properties":{"reserveAddress":{"type":"string"},"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"borrowApy":{"type":"number","description":"% per year · what this debt costs"},"usdValue":{"type":["number","null"]},"borrowFactor":{"type":["number","null"],"description":"the venue's risk weight on this debt (≥ 1) · Kamino compares debt × borrowFactor to collateral × ltv"}},"additionalProperties":true}},"marginfiBorrowed":{"type":"array","description":"marginfi debts.","items":{"type":"object","properties":{"reserveAddress":{"type":"string"},"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"borrowApy":{"type":"number","description":"% per year"},"usdValue":{"type":["number","null"]},"account":{"type":"string","description":"The marginfi account carrying this debt."}},"additionalProperties":true}},"partial":{"type":"boolean","description":"True when one of the two venues could not be read · its arrays are empty for that reason, not because the wallet holds nothing there."}},"additionalProperties":true}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_positions · both lending venues are unreachable right now. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/lev":{"post":{"operationId":"leveragedSpot","summary":"Leveraged SOL/USDC spot on Kamino · read the venue, read a position, or build an open or close for the caller to sign.","description":"One route, several actions. `venue` and `position` are reads. `setup` returns the one-time Kamino account transaction a fresh wallet signs once. `open-atomic` builds the whole position as ONE transaction on Kamino's flash loan — preferred, and the only shape with no reachable half-built state; `open` is the multi-round loop fallback for routes that will not fit a packet. `close-atomic` and `close` mirror them. A loop `close` may begin with a `repay` leg funded from the debt asset already in the wallet, and answers `legs: []` with a note when no collateral is free at the venue's loan-to-value · run `close-atomic` then, rather than the same plan again. Nothing is signed here: every build returns an unsigned transaction. GEOFENCE: `open` and `open-atomic` are refused with 451 `region_restricted` outside the permitted regions (the same gate as Kamino borrows); `close`, `close-atomic`, `position`, `venue` and `setup` are never gated, so a position can always be exited. FEES: `open-atomic` carries the 0.33% lending fee, carved out of the FRESH stake before it becomes collateral (`plan.feeBps` / `plan.feeUi`), and its internal swap leg carries no swap fee; while a fee applies, opening against pre-deposited collateral (`usePreDeposited`) is refused 409 `fee_on_predeposit` rather than under-charged. The loop `open` builds each leg with the ordinary builders, so EVERY deposit leg carries 0.33% and EVERY swap leg carries the 0.85% swap fee, each leg reporting its own `feeBps`; borrow legs carry none. `close-atomic` carries no platform fee (`plan.feeBps` 0); the loop `close` carries no lending fee but its swap legs (the collateral sold to repay, and any settleTo conversion) each carve the 0.85% swap fee. Promoted wallets pay nothing on any path. METERING: a loop `open` and a loop `close` are each charged one unit per leg against a separate 240-legs-per-minute ceiling for the key (429 rate_limited), on top of the trading lane. Fee rates are the deployment's configured ones; each build's plan.feeBps and each leg's feeBps are authoritative.","x-scope":"defi","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["venue","position","setup","open","open-atomic","close","close-atomic"]},"side":{"type":"string","enum":["long","short"],"default":"long"},"collateralUi":{"type":"number","description":"the stake, in units of `fundFrom` — SOL for a long, USDC for a short"},"leverage":{"type":"number","description":"requested. Hard ceiling 2.5x on every path — 400 bad_leverage above it, atomic and loop alike, never clamped down to it. open-atomic: built at exactly this figure, or refused 400 leverage_too_high when the venue's LTV and borrow factor cannot carry it (about 1.68x for a short today; a long's venue cap sits above 2.5x, so it never binds). open (loop): silently REDUCED to what the planner's LTV target allows (2x long at the default 0.5 target, 1.5x short at 0.75; raise ltvTarget up to 0.6 / 0.8 for 2.5x / 1.78x) — read back plan.leverage. Absent on a loop open means 2; required and above 1 on open-atomic."},"maxRounds":{"type":"integer","description":"loop rounds for the non-atomic open · the geometric tail means more rounds get closer to the requested leverage. Default 2, maximum 6 (higher is clamped, not refused)."},"fundFrom":{"type":"string","enum":["SOL","USDC"]},"slippageBps":{"type":"integer","minimum":1,"maximum":800,"default":50,"description":"How far a swap leg may fill from its quote. An OPEN accepts 1-500 (the same bound applies to the venue, position and setup actions, where it does nothing); a CLOSE accepts 1-800, because a protective close fires precisely when price is running and not filling costs the position. An out-of-range value is refused 400 bad_slippage — it is never quietly replaced, since your own arrival floor is sized on the figure you sent; a fractional value is floored to the integer below it (unlike /swap and /perps, which refuse one). Absent means 50. The build echoes back what it was actually quoted at as `plan.slippageBps`: compare it before you sign."},"dryRun":{"type":"boolean","description":"return the plan without building transactions"},"usePreDeposited":{"description":"open-atomic only. `true` stakes the WHOLE supply this wallet already has in the reserve; a number stakes up to that much. Either way it is CLAMPED to what the obligation actually holds, so you may ask for less than exists and never for more. Without it, a wallet with a supply is refused `supply_present` rather than silently leveraged. What is left over stays supplied and is reported as `preDeposited.remainderUi` — it is extra collateral in the same obligation, so an open-time check of your own should expect `stake x leverage + remainder`.","oneOf":[{"type":"boolean"},{"type":"number","minimum":0}]},"ltvTarget":{"type":"number","exclusiveMinimum":0,"exclusiveMaximum":1,"description":"open (loop) only · build to your own LTV rather than the planner's default (0.5 long, 0.75 short). Clamped to the platform's own cap of 0.6 for a long and 0.8 for a short, below the venue's liquidation line."},"settleTo":{"type":"string","enum":["SOL","USDC"],"description":"close and close-atomic · the asset the proceeds come home in. Default is the position's OWN collateral asset: SOL for a long, USDC for a short. The loop close honours either (a short settling to SOL adds a final USDC→SOL swap leg, which carries the swap fee). close-atomic can only settle to that native asset — asking a short to settle to SOL is refused 409 too_large (a second router leg does not fit the packet); run the loop close for that."}}}}}},"responses":{"200":{"description":"A read, a plan, or a plan plus unsigned transactions. The shape follows `action`.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"market":{"type":"string","description":"the Kamino market address"},"sol":{"type":"object","properties":{"reserve":{"type":"string"},"ltv":{"type":"number"},"liqThreshold":{"type":"number","description":"the LTV a position is liquidated at"},"borrowFactor":{"type":["number","null"],"description":"the venue's risk weight on BORROWING this asset (≥ 1 · SOL 1.25, USDC 1.00) · Kamino compares debt × borrowFactor to collateral × ltv. Null when unreadable, never 1."},"borrowApy":{"type":"number"},"supplyApy":{"type":"number","description":"% · what a supply here earns · the rate idle capital is parked at"},"availableUi":{"type":"number","description":"liquidity available to borrow · size against this"}},"additionalProperties":true},"usdc":{"type":"object","properties":{"reserve":{"type":"string"},"ltv":{"type":"number"},"liqThreshold":{"type":"number"},"borrowFactor":{"type":["number","null"]},"borrowApy":{"type":"number"},"supplyApy":{"type":"number"},"availableUi":{"type":"number"}},"additionalProperties":true}},"additionalProperties":true,"description":"action: venue · the reserves, their caps and their live rates"},{"type":"object","properties":{"solDeposited":{"type":"number"},"usdcDeposited":{"type":"number"},"solBorrowed":{"type":"number"},"usdcBorrowed":{"type":"number"},"deposits":{"type":"array","description":"EVERY deposit in the obligation, whatever the mint · what the four SOL/USDC amounts cannot see (a jitoSOL Multiply collateral, for one). The obligation is shared with the Lend card and with Multiply.","items":{"type":"object","properties":{"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"apy":{"type":["number","null"],"description":"the reserve's live supply rate · an APY on Kamino, an APR on marginfi"},"usdValue":{"type":["number","null"],"description":"priced by the venue's own oracle · null means unknown, never zero"}},"additionalProperties":true}},"borrows":{"type":"array","description":"EVERY borrow in the obligation.","items":{"type":"object","properties":{"mint":{"type":"string"},"symbol":{"type":"string"},"amountUi":{"type":"number"},"apy":{"type":["number","null"],"description":"the reserve's live borrow rate · what this debt costs (an APY on Kamino, an APR on marginfi)"},"usdValue":{"type":["number","null"]}},"additionalProperties":true}},"empty":{"type":"boolean","description":"No balance of any kind. NOT the same as no position — see hasPosition."},"long":{"type":"boolean","description":"SOL supplied against a USDC debt. BOTH legs, never either one."},"short":{"type":"boolean","description":"USDC supplied against a SOL debt."},"hasPosition":{"type":"boolean","description":"There is a leveraged position to manage or close. Gate on THIS, never on !empty."},"supplyOnly":{"type":"boolean","description":"A balance with no matching debt — a plain supply earning the reserve rate. Idle capital looks exactly like this, and it is not a position."},"ambiguous":{"type":"boolean","description":"The obligation is not ONE SOL/USDC position (both debts at once, or a third reserve holding or owing beside the pair). Every open and close is refused 409 ambiguous_position while true — gate on it before building."},"stats":{"description":"The obligation's own oracle health. Null when it cannot be read, which is not the same as healthy.","oneOf":[{"type":"object","properties":{"ltv":{"type":["number","null"]},"liquidationLtv":{"type":["number","null"]},"netValueUsd":{"type":["number","null"]},"depositUsd":{"type":["number","null"]},"borrowUsd":{"type":["number","null"]}},"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true,"description":"action: position · the wallet's obligation, in UI units"},{"type":"object","properties":{"owner":{"type":"string"},"price":{"type":"number","description":"the SOL/USD price the plan was sized at · 0 on a close-atomic when neither the price API nor the router answered (the close is still built, sized from the obligation)"},"atomic":{"type":"boolean","description":"true for the one-transaction path"},"venue":{"type":"object"},"preDeposited":{"description":"open-atomic · what was found supplied, what of it was staked, and what stayed.","oneOf":[{"type":"object","properties":{"suppliedUi":{"type":"number"},"usedUi":{"type":"number"},"remainderUi":{"type":"number"}},"additionalProperties":true},{"type":"null"}]},"position":{"description":"close paths · the obligation being closed.","oneOf":[{"type":"object"},{"type":"null"}]},"plan":{"description":"loop paths · the rounds, the legs and the final LTV, liquidation price and buffer. Atomic paths · { side, stakeUi, fundingMint, leverage, collateralUi, collateralMint, debtUi, debtMint, flashUi, price, feeUi, feeBps, ltv, liqPrice, bufferPct, slippageBps } · feeBps is 33 on an open for a fee-bearing wallet and 0 on a close, and slippageBps is the bound the swap legs were ACTUALLY quoted at — refuse to sign a build whose figure is not the one you sent.","oneOf":[{"type":"object"},{"type":"null"}]},"guard":{"description":"atomic paths · the money-movement envelope to re-derive before signing: the stake that may leave (`fundingMint`, `stakeBase`), the SOL allowance for fees and rent (`solAllowanceLamports`), and the obligation the transaction must leave behind (`expected.collateralBase`, `expected.debtBase`, within `tolBps`). `stakeBase` is what the WALLET SENDS this transaction — the fresh stake, in base units of `fundingMint` — and nothing else: on an open drawn on `usePreDeposited` the pre-deposited collateral is already in the reserve and never leaves the wallet, so it is NOT in this figure (size the collateral you expect on fresh + pre-deposited; bound what may leave on this). Zero on a close and on a setup.","oneOf":[{"type":"object"},{"type":"null"}]},"reference":{"description":"open-atomic dryRun · the plan's final figures without building anything.","oneOf":[{"type":"object"},{"type":"null"}]},"cost":{"description":"loop OPEN only · borrow bps per hour and the number of swap legs. Absent on a loop close.","oneOf":[{"type":"object"},{"type":"null"}]},"txBase64":{"type":["string","null"],"description":"atomic paths and setup · the single transaction, unsigned by the wallet. Nothing here is ever signed for you."},"lastValidBlockHeight":{"type":["integer","null"],"description":"atomic paths and setup · the last block height the transaction's blockhash is valid at; loop legs each carry their own."},"bytes":{"type":["integer","null"],"description":"atomic paths and setup · the serialized size, against the 1232-byte packet."},"lookupTables":{"type":["array","null"],"items":{"type":"string"},"description":"atomic paths · the address lookup tables the transaction resolves through · fetch them to decompile it before signing."},"computeUnits":{"type":["integer","null"],"description":"atomic paths · the compute-unit limit set from the simulation, with headroom."},"priority":{"description":"atomic paths and setup · the compute-unit price the transaction carries and the market read that set it (floor 30k, cap 1M microLamports/CU).","oneOf":[{"type":"object"},{"type":"null"}]},"quote":{"description":"atomic paths · the internal swap leg's router quote: { inAmount, outAmount, otherAmountThreshold, priceImpactPct, route }.","oneOf":[{"type":"object"},{"type":"null"}]},"labels":{"type":["array","null"],"items":{"type":"string"},"description":"atomic paths and setup · one label per instruction, in order (`klend:flashBorrow`, `jupiter:swap`, …) · the shape a guard checks the decompiled transaction against."},"needed":{"type":["boolean","null"],"description":"setup only · false when the wallet's one-time Kamino setup is already on chain, in which case nothing else is returned."},"legs":{"type":["array","null"],"items":{"type":"object","properties":{"kind":{"type":"string","enum":["deposit","borrow","swap","withdraw","repay"]},"txBase64":{"type":"string","description":"the leg, with the wallet's signature slot empty"},"lastValidBlockHeight":{"type":["integer","null"],"description":"the last block height this leg's blockhash is valid at"},"feeBps":{"type":"integer","description":"33 on a deposit leg, 85 on a swap leg, 0 on borrow, withdraw and repay legs; 0 everywhere for a promoted wallet"},"liqThreshold":{"type":["number","null"],"description":"deposit legs · the reserve's liquidation LTV"},"borrowFactor":{"type":["number","null"],"description":"borrow legs · the venue's risk weight on the debt"}},"additionalProperties":true},"description":"loop paths · the legs, in order, each unsigned by the wallet. A swap's amount is what the previous leg delivers, so they must be sent in sequence."},"signer":{"type":["string","null"],"description":"the wallet expected to sign · every loop leg, or the one atomic transaction."}},"additionalProperties":true,"description":"action: open / open-atomic / close / close-atomic / setup · the plan, plus unsigned transactions unless dryRun"}]}}}},"400":{"description":"bad_action, bad_side, bad_amount (a collateralUi that is not a finite number or is above 1e9 on any open, or a loop swap leg too small to carry its fee), bad_leverage (loop: 1 to 2.5; atomic: above 1 and at most 2.5 — never clamped), leverage_too_high (open-atomic: above what THIS venue's loan-to-value and borrow factor can carry — the message names the cap, including a cap at or under 1x, rather than building rungs that would revert; only a short can reach it today, near 1.7x), bad_slippage (outside 1-500 on an open or 1-800 on a close — refused rather than substituted), bad_pre_deposited, or bad_json (malformed, or not a JSON object)"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"409":{"description":"position_open (a position already exists), supply_present (capital is supplied — pass usePreDeposited or withdraw), no_position, ambiguous_position (the obligation is not ONE SOL/USDC position: both debts at once, or a third reserve holding or owing something beside the pair — a Multiply loop shares this obligation. The message lists the legs; manage it from the position card), too_large (the atomic route will not fit one packet — the body carries the measured `bytes` — OR, before anything is built and without bytes: an open-atomic whose fundFrom is not the side's collateral asset (a long funds from SOL, a short from USDC), or a close-atomic asked to settle to the other asset · run the loop for those), insufficient_collateral (close-atomic: the collateral will not cover the debt at this price), or fee_on_predeposit (a fee-bearing wallet cannot open against pre-deposited collateral · promote, or withdraw first)"},"413":{"description":"payload_too_large · the body is over 4,096 bytes."},"422":{"description":"tx_too_large · a loop swap leg cannot carry its fee inside the 1232-byte packet · a smaller stake or fewer rounds."},"428":{"description":"setup_required · this wallet needs its one-time Kamino setup first: POST { action: 'setup' }, sign it, then retry. Only open-atomic needs it; the loop open initialises the account itself."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"451":{"description":"region_restricted · leveraged positions are not available in this region, or the region could not be determined (the gate fails closed). OPENS ONLY (open, open-atomic). Closes, reads and setup are never gated, so a position can always be exited. Wallets the operator designates for testing and operation (including its own) are judged on identity rather than country."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"502":{"description":"no_price, no_route, no_reserve, router (the router refused · its HTTP status is in the message), unsafe_route (the route touched a program outside the allowlist · nothing returned), unverifiable (a loop swap leg could not be verified · nothing returned), or simulation_failed (the transaction did not simulate clean · nothing returned)"},"503":{"description":"position_unread · the wallet's obligation could not be read (an RPC failure is never taken as \"no position\") · nothing built, retry. no_lut · the market's lookup table could not be read · nothing built. no_treasury · this deployment has no fee treasury configured, so a fee-bearing open is refused rather than built unmonetised. maintenance · `open` and `open-atomic` only: the operator has paused NEW exposure (a `Retry-After` header says when to try again); `close`, `close-atomic`, `position`, `venue` and `setup` are never paused. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/vizion":{"get":{"operationId":"getVizion","summary":"VIZION's recall · the last 40 completed bars matched against all 636,079 forty-bar windows in six years of venue bars, and what followed the closest ones.","description":"THE SAME READ THE CHART DRAWS. Returns the blended five-bar continuation of the closest historical analogues, their per-step agreement, and the 32 closest matches (heaviest first) with the UTC close time of each so you can look them up. The query is the last 40 COMPLETED bars; the bar in progress is never in it.\n\nWHICH FIVE BARS. `path[i]` and `analogs[].path[i]` are bars +2 to +6 counted from `barT`, the close of the last completed bar, and every one is measured from `anchorT` — the close of the bar NOW FORMING, which is the price the chart draws from and the price you hold. Not from `barT`'s close. The library stores six continuation bars and spends the first on that anchor; the five that remain are the five columns on the chart.\n\nREAD `accuracy` BEFORE USING `path`. Measured walk-forward over six years at the 21-bar length the library first shipped with: library depth takes best-match similarity from 0.676 to 0.838 — history genuinely recognises the present, which is what this endpoint answers. It does NOT make the read directional: direction of the next five bars is 50.0% at every library depth and 45.7% on matches above 0.9 similarity; re-measured at the served 40-bar length and the served alignment over EVERY causal bar in the last 12% of the library (76,329 queries at stride 1, 381,645 steps, 265 days), the blend is right 49.8% of the time and the single drawn precedent 48.5%. And the path is not a forecast — on 71,424 out-of-sample bars a FLAT LINE predicts the next five bars better than this path (by 4.4% at 21 bars; by 3.5% for the blend and 46% for the drawn precedent at 40) and better than a gradient-boosted model trained on the same six years (by 4.0%). The current price is the best available estimate of where price will be.\n\nTHE ENVELOPE IS THE ACCURATE PART, AND IT IS A PER-STEP FIGURE. The min–max of `analogs[].path` at each step (not a served field; compute it) held the realised move 93.4% of the time AT A GIVEN STEP — but the move stayed inside it at all five steps at once only 82.0% of the time. A stop is a path claim, so size from the per-step figure and STOP from /signals `reach`, whose band is calibrated as a path (94.9% one bar out to 94.0% at five, wicks included, over six years). Both envelope figures are served, labelled, in `accuracy`.\n\n`agreement` is the load-bearing field — it measures how much the recalled set agrees with ITSELF, i.e. whether this is a configuration history knows, not confidence in a direction. Every figure is served in `accuracy`, labelled by the object and the length it was measured on.","x-scope":"read","responses":{"200":{"description":"The recall","content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"integer"},"barT":{"type":"integer","description":"close time of the newest completed bar the query was taken from"},"anchorT":{"type":"integer","description":"UTC close every path entry is measured FROM · one bar after `barT`, i.e. the bar still forming, whose live price is the anchor"},"path":{"type":"array","items":{"type":"number"},"description":"blended continuation · cumulative LOG-return from `anchorT`, one entry per bar ahead (bars +2 to +6 from `barT`), in the query's own volatility scale. Not a forecast (see `accuracy`)."},"agreement":{"type":"array","items":{"type":"number"},"description":"per step, aligned with `path` · 1 when every recalled analogue continued the same way and 0 at a dead split"},"horizon":{"type":"integer","description":"how far the path stays coherent, 0..5 · 0 means history recognises the moment but disagrees on what follows"},"bestSim":{"type":"number","description":"cosine similarity of the closest historical shape, -1..1 · an exact cosine of the quantised vectors, so a window matched against itself is 1.000000"},"strength":{"type":"number","description":"match quality blended with near-term agreement, 0..1"},"simRank":{"type":"number","description":"where bestSim ranks against six years of best matches, 0..1 · measured on every causal bar of the late window at the served length (76,329 queries at stride 1); 0.995 means closer than 99.5% of moments"},"rhyme":{"type":"boolean","description":"true when simRank is at least 0.995 · the lens announces the precedent. Fires 1.45 times a day, measured."},"analogs":{"type":"array","description":"the 32 closest matches, heaviest first","items":{"type":"object","properties":{"at":{"type":"integer","description":"UTC close time of that historical window's last bar · its continuation is anchored one bar later, exactly as the query's is at `anchorT`"},"sim":{"type":"number"},"weight":{"type":"number"},"path":{"type":"array","items":{"type":"number"},"description":"that window's own continuation, rescaled to the query's volatility, re-anchored the same way as `path`"}},"additionalProperties":true}},"library":{"type":"object","properties":{"windows":{"type":"integer"},"from":{"type":"integer","description":"UTC CLOSE of the oldest window's last bar · same convention as `barT` and `analogs[].at`"},"to":{"type":"integer","description":"UTC CLOSE of the newest window's last bar"},"lookback":{"type":"integer","description":"40"},"horizon":{"type":"integer","description":"5 · the number of bars actually projected. The file stores six; the first is spent on `anchorT`."},"ageDays":{"type":"number","description":"how far behind the present the library is, in days"}},"additionalProperties":true},"basis":{"type":"string","description":"one paragraph stating what the path is and is not"},"cached":{"type":"boolean","description":"true when the answer came from the per-bar store; false when this call had to compute it"},"stale":{"type":"boolean","description":"true when the store held only the PREVIOUS bar's answer · a different precedent for a different query, not an older view of this one. Re-ask in a few seconds for the current bar's."},"accuracy":{"type":"object","properties":{"directionNext5":{"type":"number","description":"21-bar study · direction of the blended path over the next five bars, all depths (0.5003)"},"directionNext5At40":{"type":"object","properties":{"blend":{"type":"number"},"drawnPrecedent":{"type":"number"}},"additionalProperties":true},"directionOnClosestMatches":{"type":"number","description":"21-bar study · on matches above 0.9 similarity (0.457)"},"pathVsFlatLine":{"type":"string","description":"the error of the path against a flat line, by object and length"},"bandContainmentPerStep":{"type":"number","description":"21-bar study · the recalled set's envelope AT A GIVEN STEP (0.943)"},"bandContainmentAt40PerStep":{"type":"number","description":"served length and alignment · the recalled set's envelope AT A GIVEN STEP (0.934). Size from this."},"bandContainmentAt40Path":{"type":"number","description":"served length and alignment · the move stayed inside the envelope at ALL FIVE steps at once this often (0.820). This, not the per-step figure, is what a stop is judged by."},"stopsFrom":{"type":"string","description":"where to take a stop from, and why not from the per-step envelope"},"measuredOn":{"type":"string"},"readAs":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}}},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_match · the pattern library is not present on this deployment, or the venue reference is unavailable (one code for both). On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/climax":{"get":{"operationId":"getClimax","summary":"The climax rule's live view · whether a call is issued right now, and every input the rule decides on.","description":"The same function the trading bot reads. A call is issued only when a completed Binance 5-minute SOL bar prints at least 5x its median volume and 2.5x its usual range, the magnitude forecast clears the causal gate, the prior day trended down, the 30-day trend is not up (for a short fade), and the bar falls inside the session. `now` carries every input continuously — the volume and range ratios against their thresholds, the forecast against the cut, the trend and session flags — so a caller can see how close the rule is without waiting for it to fire. `margin` is the forecast divided by the cut and `tier` is `strong` at 2x or more. SOLUSDT is the only symbol.","x-scope":"read","parameters":[{"name":"symbol","in":"query","schema":{"type":"string","default":"SOLUSDT"}}],"responses":{"200":{"description":"the live rule view, its call or the reason there is none","content":{"application/json":{"schema":{"type":"object","properties":{"symbol":{"type":"string","description":"SOLUSDT"},"at":{"type":"integer","description":"Unix seconds of this read."},"live":{"type":["number","null"],"description":"The reference venue's live price · null when the reference answered without one. The bars and the rule's verdict are still served: a missing live quote is not a missing read."},"bars":{"type":"integer","description":"Reference bars behind the read."},"source":{"type":"string","enum":["binance","venue"],"description":"Which source the CALL is judged on. `binance` only when the operator's Binance feed is fresh — the sole source a call is ever issued from. `venue` otherwise: the reference bars are still served for diagnostics, but no call is issued from them, whichever exchange they came from."},"feedFresh":{"type":"boolean","description":"True when the operator's Binance feed is fresh · exactly the condition under which `source` is `binance`."},"referenceVenue":{"type":["string","null"],"description":"The exchange the reference BARS on show came from (`binance`, `coinbase`, or null when the reference did not say) · a fact about the diagnostic series, never the source the call is judged on."},"feedBarT":{"type":["integer","null"],"description":"Open time of the last completed feed bar."},"lagging":{"type":"boolean","description":"True when the feed's newest bar is one behind the venue's (the seconds after a five-minute boundary). The feed is still fresh and a call can still be issued from it; two bars behind, or a push older than 30 s, is stale and `source` becomes `venue`. Informational, not a gate."},"gateReady":{"type":"boolean","description":"False while the feed is fresh but has not yet accumulated the 60 days of its own predictions the gate cut needs · no call can fire, and that is an outage rather than a quiet market."},"diag":{"type":"object","properties":{"bars":{"type":"integer"},"volumeBars":{"type":"integer","description":"How many of those bars carry volume · the rule cannot fire without it."},"lastBarT":{"type":["integer","null"]},"lastBarAgeSec":{"type":["integer","null"]},"lastVRatio":{"type":["number","null"],"description":"Last bar's volume against its median."},"lastRRatio":{"type":["number","null"],"description":"Last bar's range against its ATR."},"blind":{"type":"boolean","description":"Too few volume-bearing bars to judge · the rule cannot fire. `/signals` reports this through `health.why` instead."}},"additionalProperties":true},"now":{"description":"Every input the rule decides on, continuously · ratios against thresholds, forecast against cut, trend and session flags.","oneOf":[{"type":"object"},{"type":"null"}]},"climax":{"description":"The climax bar when one has printed.","oneOf":[{"type":"object"},{"type":"null"}]},"call":{"description":"The rule's call · same shape as `/signals` `call` with `source: climax`, and never null here (a declined call is an object with `status` weak or none). Carries NO probability: the rule has a gate, not a probability model. `trustForSec` counts down from 120 s after the trigger bar's close; `horizonMin` is 60.","oneOf":[{"type":"object"},{"type":"null"}]},"magnitude":{"description":"The causal forecast and the cut it is judged against.","oneOf":[{"type":"object"},{"type":"null"}]},"evidence":{"description":"The rule's six-year record as measured, and the strong tier's.","oneOf":[{"type":"object"},{"type":"null"}]}},"additionalProperties":true}}}},"400":{"description":"bad_symbol · symbol is not SOLUSDT, the only one the magnitude model was fitted for; the message says so."},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"502":{"description":"no_reference · the venue reference is unavailable · nothing to judge, so nothing is served."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/signals":{"get":{"operationId":"getSignals","summary":"VIZION signals for SOL · next-bar lean, forming-bar call and the reach band, each with its MEASURED accuracy, plus the fee economics that decide whether a bot can pay for trading it.","description":"Three reads. `next` is the direction of the next bar as the platform's own live ledger grades it against the DEX bar the chart draws (62.4% overall against a 51.6% baseline, 68.3% at |lean| ≥ 2.4 · n 500 / 63 on 2026-09-06, re-graded continuously at /api/vizion/score; the edge is spent in one bar, and `tradeable` is always false). `now` is the forming bar's close relative to its OPEN (90-93% walk-forward on venue bars, not re-measured on this feed) and is a level, NOT an entry · it is 44% right measured from the current price. `reach` is the verified read: price stayed inside this band construction 94.0-94.9% of the time by depth, wicks included, over six years of venue bars; built here from the DEX feed's sigma (about 0.86 of the venue's), so size and place stops from it with that margin. `economics.breakEvenAccuracy` above 1 means no accuracy can pay the round-trip fee at this horizon. Read `disclosures` before trading anything here.","x-scope":"read","parameters":[{"name":"tf","in":"query","schema":{"type":"string","enum":["5m","15m"],"default":"5m"}}],"responses":{"200":{"description":"Signal","content":{"application/json":{"schema":{"type":"object","properties":{"market":{"type":"string","description":"SOL"},"tradeable":{"type":"boolean","description":"Always false · measured. next describes the platform's candle feed catching up to the market, not price. See disclosures."},"tf":{"type":"string"},"t":{"type":"integer","description":"The OPEN time of the newest DEX candle the signal is anchored on (the forming bar), unix seconds."},"anchorAgeSec":{"type":"integer","description":"Seconds between that open and this read · how far into the bar the read is, plus the feed's lag. Judge the signal from now, never from t."},"spot":{"type":"number","description":"The DEX price the chart draws."},"reference":{"type":["number","null"],"description":"The reference venue's live price · where a perp actually fills. Null when no fresh pair."},"mark":{"type":["number","null"],"description":"The live mark the lean is computed from, distinct from spot (the last candle, which the feed lags). Null when no fresh pair."},"next":{"type":"object","properties":{"dir":{"type":"integer","enum":[-1,0,1],"description":"+1 up, -1 down, 0 no claim (stale pair, broken quote, or no lean)."},"lean":{"type":"number","description":"Standard deviations the venue pair is leaning. Sign is the call, magnitude is belief."},"p":{"type":"number","description":"The live ledger's hit rate for this |lean| band against the DEX bar the chart draws (GET /api/vizion/score byStrength, read 2026-09-06 on 500 graded calls; the bands are small and not monotone). 0.5 when dir is 0. Never 1."},"venueP":{"type":"number","description":"Pinned at 0.5 at every strength since 2026-08-29, by decision: graded forward from the observation moment, the venue's next bar reads 45-51% by band (276 calls; the live ledger's 4,526 forward verdicts read 47.3%). Where a perp fills. A perp gated on it will not trade, which is the instruction."},"confidence":{"type":"string","description":"strong | moderate | weak | none"},"expectedMove":{"type":"number","description":"Expected next-bar body, log-return."},"fresh":{"type":"boolean","description":"Whether the deviation was read from a live pair (under 30s old)."}},"additionalProperties":true},"now":{"description":"The forming bar's call, or null below 90%. A LEVEL relative to the bar's open · not an entry.","oneOf":[{"type":"object","properties":{"dir":{"type":"integer"},"p":{"type":"number"},"elapsed":{"type":"number"},"z":{"type":"number"},"open":{"type":"number"},"note":{"type":"string"}},"additionalProperties":true},{"type":"null"}]},"reach":{"type":"object","properties":{"containment":{"type":"number"},"band":{"type":"array","items":{"type":"number"},"description":"Half-width per bar ahead, log-return."},"levels":{"type":"array","items":{"type":"object","properties":{"high":{"type":"number"},"low":{"type":"number"}},"additionalProperties":true}},"sigma":{"type":"number"},"measured":{"type":"array","items":{"type":"number"},"description":"Measured path containment by depth, wicks included · 0.949, 0.945, 0.944, 0.941, 0.94 on six years of venue bars at the deployed multiple."}},"additionalProperties":true},"economics":{"type":"object","properties":{"roundTripFee":{"type":"number"},"meanAbsMove":{"type":"number"},"breakEvenAccuracy":{"type":"number","description":"Accuracy a symmetric one-bar trade needs to pay the fee. >1 means impossible."}},"additionalProperties":true},"disclosures":{"type":"array","items":{"type":"string"}},"version":{"type":"integer","description":"Payload version. Fields are added, never repurposed."},"barSeconds":{"type":"integer","description":"Seconds per bar for `tf` · 300 at 5m."},"health":{"type":"object","properties":{"ok":{"type":"boolean","description":"True only when every input the climax rule depends on is present and fresh: `climaxSource` is `binance`, `climaxNow` is present, `magnitude.cutBps` is positive, and the reference bars are neither blind nor absent. GATE ON THIS: a call carried by an unhealthy answer is not the rule."},"why":{"type":"array","items":{"type":"string"},"description":"What is missing when ok is false, one line each: `climax source <name>`, `no live rule view (feed stale or absent)`, `no magnitude cut`, `reference is blind`, `no reference bars`."}},"additionalProperties":true},"sources":{"type":"object","properties":{"candles":{"type":"integer","description":"Candles behind the DEX read."},"reference":{"type":"integer","description":"Reference bars behind the climax read."},"pairAgeMs":{"type":["integer","null"],"description":"Age of the venue pair the lean is computed from · null when there was no fresh pair."},"referenceOnline":{"type":"boolean"}},"additionalProperties":true},"climaxSource":{"type":"string","enum":["binance","coinbase"],"description":"Which source the climax CALL is judged on. `binance` ONLY when the operator's Binance feed is fresh — the sole source a call is ever issued from, and the same rule /climax decides on (`feedFresh` says the condition outright). Otherwise the literal `coinbase`, this route's label for the reference-bar fallback whatever exchange those bars came from (`referenceVenue` names it): the bars are still computed for diagnostics, NO call is issued from them, and `health.why` carries `climax source coinbase`."},"feedFresh":{"type":"boolean","description":"True when the operator's Binance feed is fresh · exactly the condition under which `climaxSource` is `binance` and a climax call can be issued."},"referenceVenue":{"type":["string","null"],"description":"The exchange the reference BARS on show came from · `binance`, `coinbase`, `none` (the reference read answered but every venue refused — the bars are empty and `sources.reference` is 0), or null when the read itself failed or timed out. A fact about the diagnostic series, never the source the call is judged on."},"magnitude":{"description":"The causal magnitude forecast for the next horizon, and the trailing cut it is judged against. Null when the model has no answer.","oneOf":[{"type":"object","properties":{"horizonMin":{"type":"integer"},"meanBps":{"type":"number","description":"The forecast itself, bps."},"cutBps":{"type":["number","null"],"description":"The causal trailing q90 cut · the gate line. `meanBps / cutBps` is the margin."},"gateK":{"type":["number","null"],"description":"The multiplier the forecast must clear · 1.15 as served."},"decile":{"type":["integer","null"],"description":"Which decile of its own history this forecast sits in."},"q50Bps":{"type":"number"},"q75Bps":{"type":"number"},"q90Bps":{"type":"number"},"p30":{"type":"number"},"p50":{"type":"number"},"basis":{"type":"string"}},"additionalProperties":true},{"type":"null"}]},"call":{"description":"THE CALL. Either the climax rule's (`source: climax`, present whenever a climax bar has printed — it outranks the lean model even when it declines) or the lean model's, and they are different things that both say `status`. The lean model's call carries NO `source` field: absence of `source` means the lean model. A climax call carries NO probability — the rule has a gate, not a probability model — so `p20`/`p30` are absent on it and a probability floor must not be applied to it. Before any model exists the call is `{ status: 'none', dir: 0, basis: 'no model yet' }`.","oneOf":[{"type":"object","properties":{"source":{"type":"string","enum":["climax"],"description":"Present ONLY on the climax rule's call. Absent on the lean model's."},"status":{"type":"string","description":"call | weak | none"},"dir":{"type":"integer","enum":[-1,0,1]},"expectedBps":{"type":["number","null"],"description":"Expected move · for a climax, min(take-profit, forecast)."},"horizonMin":{"type":["integer","null"]},"trustForSec":{"type":["integer","null"],"description":"Seconds this call remains actionable. Null means untracked, NOT expired."},"decay":{"description":"climax only · HOW MUCH OF THE RECORD IS STILL ON THE TABLE at this call's age. Every figure in `evidence` is measured entering at the trigger bar's CLOSE; re-entered later the same calls returned +34.3 bps at +0 min, +22.1 at +1, +25.4 at +2, +13.4 at +3, +9.1 at +4, +5.2 at +5 and −0.6 at +10, with the hit rate falling 67.1% → 51.0% (149 served calls on one-minute bars, 2025-09 → 2026-08). Act on it at the close or size for the delay; do not read a five-minute-old call as the +36 bps figure. Levels are approximate (perpetual bars) — the shape is what is trusted, and the table is NOT interpolated because +1 and +2 invert inside the noise.","oneOf":[{"type":"object","properties":{"elapsedMin":{"type":"integer","description":"whole minutes since the bar closed"},"freshness":{"type":"string","description":"fresh (0-2 min) | late (3-4) | spent (5+)"},"shareOfEdge":{"type":"number","description":"measured net at this delay as a share of the at-close net; below 0 means worse than not trading"},"netBpsAtDelay":{"type":"number"},"hitAtDelay":{"type":"number"},"note":{"type":"string","description":"one line stating the same in words"}},"additionalProperties":true},{"type":"null"}]},"margin":{"type":["number","null"],"description":"climax only · forecast / cut. `tier` is `strong` at 2 or more."},"tier":{"type":["string","null"],"description":"climax only · `strong` or `call`."},"session":{"description":"climax only (present on a declined call too) · the session test for the trigger bar's UTC close hour: `ok` says whether it passed; `via` is `session` (12-24 UTC), `block` (a walk-forward morning block for the year) or null when it failed.","oneOf":[{"type":"object","properties":{"hour":{"type":"integer"},"ok":{"type":"boolean"},"via":{"type":["string","null"],"enum":["session","block",null]}},"additionalProperties":true},{"type":"null"}]},"exits":{"description":"climax only · the rule's own take-profit and stop, bps of price.","oneOf":[{"type":"object","properties":{"tpBps":{"type":"number"},"stopBps":{"type":"number"}},"additionalProperties":true},{"type":"null"}]},"gate":{"description":"climax only · which gate conditions passed.","oneOf":[{"type":"object"},{"type":"null"}]},"p20":{"type":["number","null"],"description":"lean model only · calibrated fade probability at +20 minutes. ABSENT on a climax call."},"p30":{"type":["number","null"],"description":"lean model only · calibrated fade probability at +30 minutes. ABSENT on a climax call."},"basis":{"type":["string","null"]},"inputs":{"description":"lean model only · what it decided on.","oneOf":[{"type":"object"},{"type":"null"}]},"model":{"description":"lean model only · the training days, the held-out test day and its held-out results at each threshold.","oneOf":[{"type":"object"},{"type":"null"}]},"magnitude":{"description":"The magnitude read the call was judged with · the same object as the top-level `magnitude`.","oneOf":[{"type":"object"},{"type":"null"}]},"evBps":{"description":"Expected value of acting, in bps. Lean model: `{ gross, spot, perp }` — (2p − 1) × E|move|, then after a 1.5-bps spot and a 12-bps perp round trip. CLIMAX call: `{ gross }` is what calls of this TIER actually returned over the six years, entering at the trigger bar's close — +121.2 bps for `strong` (105 calls) and +19.5 for an ordinary `call` (the balance of the same 648; the +36 headline is the two together and is lifted by the strong sixth, so do not apply it to an ordinary call). Not an EV formula: it previously read (2 × 0.66 − 1) × forecast, whose 0.66 was not the rule's hit rate (64.8 % is) and which assumed a win and a loss are the same size when they are +147 against −168. Multiply by nothing; read `decay` beside it for how much of this survives a late entry. Null when the climax declined.","oneOf":[{"type":"object"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"climax":{"description":"The climax BAR when one has printed · its ratios, range and gate verdicts. Null when none has.","oneOf":[{"type":"object"},{"type":"null"}]},"climaxNow":{"description":"Every input the climax rule decides on, continuously — the volume and range ratios against their thresholds, the forecast against the cut, the trend and session flags. Absent means the feed is stale, and no call should be acted on.","oneOf":[{"type":"object"},{"type":"null"}]},"venue":{"description":"The reference venue's own forward-graded band for this read · the fade of the DEX lean, spot only.","oneOf":[{"type":"object"},{"type":"null"}]},"liq":{"description":"Liquidation map from the reference venue · where leveraged positions sit.","oneOf":[{"type":"object"},{"type":"null"}]},"positioning":{"description":"Binance futures positioning, from the operator's sampler (`feed:binfut`, pushed about every ten seconds). A fixed set of fields, each a number or null: `mark`, `index`, `premium` (fraction), `funding` (last settled 8h rate, fraction), `nextFundingT`, `oi`, `oiHist`, `oiHistT`, `barT`, `c`, `takerRatio`, `takerEp`, `takerEpT`, `topPosRatio`, `topPosT`, `topAcctRatio`, `topAcctT`, `globalRatio`, `globalT`; `lagSec` ({ oiHist, taker, ratio }, how far behind wall-clock Binance's `futures/data` labels are — those three series are minutes late by construction); `t` the sampler's own clock and `at` the platform's ingest stamp; `ageSec` measured from `at`. NULL, never a partial object, when no reading has a mark.","oneOf":[{"type":"object"},{"type":"null"}]}},"additionalProperties":true}}}},"400":{"description":"bad_tf · tf is not 5m or 15m."},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_history · fewer than 80 candles are available for the market. no_signal · the engine could not produce a signal from the available data. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/feeds/{name}":{"get":{"operationId":"getFeed","summary":"The latest reading of an operator-pushed feed, and optionally its recent history.","description":"Readings the platform cannot take for itself — exchange positioning that is geo-blocked from the platform's egress, a long-running liquidation watch — are sampled on the operator's own machines and pushed here at each sampler's own cadence — climax every five seconds (every second in a bar's first ten), binfut about every ten, the scans less often (the POST side takes the operator's ingest secret, not an API key). A keyed read returns the latest object with its age, and up to the 300 most recent readings when `n` is given (the ring keeps the last 2,880). Feed names are lowercase letters, digits and dashes, at most 32 characters. A key reads the PUBLISHED feeds, which are the market ones: `binfut` (Binance futures positioning), `cascades`, `climax` (the climax rule's own Binance series), `custody` (perp custody crowding), `flow` (DEX taker flow) and `liqmap` (the liquidation scan). Any other name — one that was never pushed, and one the operator keeps to itself — answers 404 `no_feed` alike, so the namespace says nothing about what else is in it. `at` on every reading is the PLATFORM's clock, stamped at ingest, which is what `ageSec` is measured from: a sampler's own timestamp inside the reading cannot be used to judge whether that sampler has stalled.","x-scope":"read","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","enum":["binfut","cascades","climax","custody","flow","liqmap"]}},{"name":"n","in":"query","schema":{"type":"integer","minimum":0,"maximum":300,"default":0},"description":"How many of the most recent READINGS to include, 0-300 (a larger value is clamped, not refused) · one entry per push, newest first, so on a feed pushed every 5 s (climax) 300 entries is about 25 minutes and on one pushed every 10 s (binfut) about 50. 0 omits `history`."}],"responses":{"200":{"description":"The feed · `latest` is null when a published feed has had nothing pushed to it lately (a reading is kept 24 h).","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"latest":{"description":"The most recent reading, with the unix-seconds `at` the platform stored it.","oneOf":[{"type":"object"},{"type":"null"}]},"ageSec":{"type":["integer","null"]},"history":{"type":"array","items":{"type":"object"},"description":"Present only when n > 0 · the n most recent readings, newest first."}},"additionalProperties":true}}}},"400":{"description":"bad_name · not lowercase letters, digits and dashes, or longer than 32 characters."},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"404":{"description":"no_feed · not one of the published feeds. Operator-internal names answer this too, so an unpublished feed cannot be discovered by probing."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_store On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/swap/quote":{"post":{"operationId":"quoteSwap","summary":"Quote a swap (inclusive fee: amountBase is the total committed).","x-scope":"trade","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"inputMint":{"type":"string"},"outputMint":{"type":"string"},"amountBase":{"type":"string","pattern":"^[0-9]+$","description":"Total input to commit, in base units (the fee is carved from this)."},"slippageBps":{"type":"integer","minimum":1,"maximum":5000,"default":50,"description":"Whole basis points. An out-of-range or non-integer value is refused 400 bad_slippage — it is NEVER quietly replaced with the default, because a bound you did not choose is a fill you did not authorise. Omit the field to take 50."}},"required":["inputMint","outputMint","amountBase"],"additionalProperties":false}}}},"responses":{"200":{"description":"Quote + fee · the quote is for `amountBase − feeBase`, the amount that actually swaps.","content":{"application/json":{"schema":{"type":"object","properties":{"quote":{"type":"object"},"fee":{"$ref":"#/components/schemas/Fee"}},"additionalProperties":true}}}},"400":{"description":"bad_mint, bad_amount (not a positive base-unit integer string, or too small to cover the fee), or bad_slippage (outside 1-5000 or not a whole number)"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"413":{"description":"payload_too_large · the body is over 2,048 bytes."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"502":{"description":"upstream · the router refused the pair or the size · the message carries its reason."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/swap/build":{"post":{"operationId":"buildSwap","summary":"Quote + build an unsigned swap in one call.","x-scope":"trade","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"inputMint":{"type":"string"},"outputMint":{"type":"string"},"amountBase":{"type":"string","pattern":"^[0-9]+$","description":"Total input to commit, in base units."},"slippageBps":{"type":"integer","minimum":1,"maximum":5000,"default":50,"description":"Whole basis points. An out-of-range or non-integer value is refused 400 bad_slippage — it is NEVER quietly replaced with the default, because a bound you did not choose is a fill you did not authorise. Omit the field to take 50."}},"required":["inputMint","outputMint","amountBase"],"additionalProperties":false},"example":{"inputMint":"So11111111111111111111111111111111111111112","outputMint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amountBase":"10000000"}}}},"responses":{"200":{"description":"Swap transaction for the wallet to sign · the 0.85% fee is carved out of `amountBase` and rides the transaction as a transfer to the treasury. The reliable-tier router quote (deterministic AMMs) is tried first, then an unrestricted one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapBuild"}}}},"400":{"description":"bad_mint, bad_amount (too small to cover the fee), or bad_slippage (outside 1-5000 or not a whole number)"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"413":{"description":"payload_too_large · the body is over 2,048 bytes."},"422":{"description":"tx_too_large · the route is too large to carry the fee inside the packet · try a smaller amount or a simpler pair."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"502":{"description":"no_route, unsafe_route (the router's transaction touched a program outside the allowlist · nothing returned), unverifiable, or upstream (the router refused the quote or the build · the message carries its reason)"},"503":{"description":"no_treasury · this deployment has no fee treasury configured, so a fee-bearing build is refused rather than built unmonetised. maintenance · the operator has paused NEW exposure (a `Retry-After` header says when to try again); /execute, cancels, closes, withdrawals and repayments are never paused. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/orders":{"post":{"operationId":"manageOrders","summary":"Create / cancel / list limit and DCA orders (returns unsigned txs for create & cancel).","description":"Create escrows the FULL amount you name (`makingAmountBase` / `inAmountBase`) with the order program and adds the 0.85% platform fee ON TOP, transferred in the same transaction the moment the order is placed (`fee.at: placement`). Size the wallet for `amount × 1.0085`. The placement fee is not refunded if the order never fills, expires or is cancelled; cancel returns the escrowed amount only. Promoted wallets pay nothing.","x-scope":"trade","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["limit","dca"]},"action":{"type":"string","enum":["create","cancel","list"]},"inputMint":{"type":"string"},"outputMint":{"type":"string"},"makingAmountBase":{"type":"string","pattern":"^[0-9]+$","description":"limit create · input sold, base units."},"takingAmountBase":{"type":"string","pattern":"^[0-9]+$","description":"limit create · minimum output, base units (sets the price)."},"inAmountBase":{"type":"string","pattern":"^[0-9]+$","description":"dca create · total input committed, base units."},"numberOfOrders":{"type":"integer","minimum":2,"maximum":1000},"intervalSeconds":{"type":"integer","minimum":60,"maximum":7776000,"description":"dca create · seconds between fills, 60s to 90 days."},"order":{"type":"string","description":"cancel · the order account address."},"expiredAtUnix":{"type":"integer","description":"limit create · optional expiry, unix seconds. A value in the past (or not in the future) is silently dropped and the order is created without an expiry."}},"required":["kind","action"],"additionalProperties":false},"examples":{"limitCreate":{"summary":"Sell 0.1 SOL when it is worth 25 USDC","value":{"kind":"limit","action":"create","inputMint":"So11111111111111111111111111111111111111112","outputMint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","makingAmountBase":"100000000","takingAmountBase":"25000000"}},"dcaCreate":{"summary":"DCA 300 USDC into SOL over 6 hourly fills","value":{"kind":"dca","action":"create","inputMint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","outputMint":"So11111111111111111111111111111111111111112","inAmountBase":"300000000","numberOfOrders":6,"intervalSeconds":3600}},"list":{"summary":"List open limit orders","value":{"kind":"limit","action":"list"}},"cancel":{"summary":"Cancel one order · replace the placeholder with an address from action:list before sending (the placeholder itself is refused 400 bad_order)","value":{"kind":"limit","action":"cancel","order":"<order account address from list>"}}}}}},"responses":{"200":{"description":"create (limit): { txBase64, lastValidBlockHeight, requestId, orderKey, order, fee, signer } — `orderKey` and `order` both carry the order account. create (dca): { txBase64, lastValidBlockHeight, requestId, orderKey: null, fee, signer } — no `order` field at all. cancel: { txBase64, requestId, signer } · no fee. list: { orders: [...] }.","content":{"application/json":{"schema":{"type":"object","properties":{"txBase64":{"type":"string"},"lastValidBlockHeight":{"type":["integer","null"]},"requestId":{"type":["string","null"]},"orderKey":{"type":["string","null"],"description":"create · the order account for a limit order; null for a DCA."},"order":{"type":["string","null"],"description":"limit create · the order account (the same value as `orderKey`). ABSENT on a DCA create."},"fee":{"$ref":"#/components/schemas/Fee"},"signer":{"type":"string"},"orders":{"type":"array"}},"additionalProperties":true}}}},"400":{"description":"bad_request (kind/action), bad_mint, bad_amount, bad_orders (2–1000), bad_interval (60s–90d), bad_order (cancel: not an address), or bad_json"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"413":{"description":"payload_too_large · the body is over 4,096 bytes."},"422":{"description":"tx_too_large · the order transaction will not carry the platform fee inside the 1232-byte packet · a smaller amount or a simpler pair. The same refusal /swap/build makes."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"502":{"description":"upstream · the order API refused (a minimum total order value — its refusal names 100 USDC — no route, bad pair) · the message carries its reason; or no_tx"},"503":{"description":"no_treasury · this deployment has no fee treasury configured, so a fee-bearing create is refused rather than built unmonetised. maintenance · creates only: the operator has paused NEW exposure (a `Retry-After` header says when to try again); cancel and list are never paused. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/perps/build":{"post":{"operationId":"buildPerp","summary":"Open or close a SOL-PERP position, long or short. Opens are region-gated; closes never are.","description":"Builds an unsigned Jupiter Perpetuals market REQUEST for the key's wallet: the transaction escrows the collateral and records intent, and a Jupiter keeper fills it against the oracle in a later transaction — or, if the price has moved past `slippageBps`, closes the request and REFUNDS the collateral. `action` and `side` are strict: any value other than the documented words is refused 400 (`bad_action` / `bad_side`), never coerced; an absent `action` means open and an absent `side` means long. Both sides fund with SOL (`collateralSol`); a short's margin converts to the stable custody inside the keeper's fill, bounded by a minimum-out. GEOFENCE: an OPEN is refused 451 `region_restricted` from a restricted region or when no country can be determined; a CLOSE is built for anyone who holds the position, from anywhere. FEE: an open pays 0.10% of `sizeUsd` in SOL at the server's mark, on top of the collateral (`feeLamports`); a close pays nothing. The fee settles when the request transaction lands and is NOT returned if the keeper later closes the request and refunds the collateral. `priceUsd` is optional and never prices the fee — ON AN OPEN the server reads the mark itself and refuses a caller price more than 5% from it (`price_off_mark`). A close is not band-checked: a `priceUsd` sent on a close is used as-is to set the keeper's fill bound, so send the live mark or omit it; when the mark cannot be read a close requires `priceUsd` (400 `bad_price`). 503 `unavailable` while perpetuals are switched off on this deployment.","x-scope":"perps","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["open","close"]},"side":{"type":"string","enum":["long","short"],"default":"long"},"collateralSol":{"type":"number","description":"open · SOL collateral."},"sizeUsd":{"type":"number","description":"open · position notional in USD."},"sizeUsdDelta":{"type":"number","description":"close · partial size to close (omit for entire)."},"entirePosition":{"type":"boolean","description":"close · close the whole position."},"priceUsd":{"type":"number","description":"Optional current SOL mark (USD) · the server uses the live mark when omitted."},"slippageBps":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Whole basis points, and how far past the mark the keeper may still fill; beyond it the request is closed and the collateral refunded. Out of range is refused 400 bad_slippage, never replaced with the default. Omit the field to take 50."}},"required":["action"],"additionalProperties":false},"example":{"action":"open","collateralSol":0.05,"sizeUsd":15}}}},"responses":{"200":{"description":"Unsigned perps request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerpBuild"}}}},"400":{"description":"bad_action (not open|close), bad_side (not long|short), bad_json, bad_collateral, bad_size, bad_leverage (over 100×), bad_price, bad_slippage (outside 1-1000 or not a whole number), or price_off_mark (open · priceUsd more than 5% from the server's mark)"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"413":{"description":"payload_too_large · the body is over 2,048 bytes."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"451":{"description":"region_restricted · perpetual OPENS are not available in the caller's region (or the region could not be determined). Closes are never gated. Wallets the operator designates for testing and operation (including its own) are judged on identity rather than country."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"unavailable (perpetuals are not enabled on this deployment), no_price (the SOL mark could not be read · opens only), or no_treasury (this deployment has no fee treasury configured · fee-bearing opens only) On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/defi/build":{"post":{"operationId":"buildDefi","summary":"Build a lending or liquidity transaction (Kamino / marginfi / Meteora). Actions are per venue; Kamino borrow is region-gated.","description":"The exact builders the terminal uses. ACTIONS ARE STRICT AND PER VENUE: meteora and marginfi accept `deposit` | `withdraw`; kamino accepts `deposit` | `withdraw` | `borrow` | `repay`. Any other word is refused 400 `bad_action` by name (never coerced to a deposit); an absent action means deposit. FEE: a deposit carries 0.33%, CARVED OUT of `amountUi` — the venue receives the remainder (`depositUi`) and the fee rides the same transaction. Withdraw, borrow and repay carry no fee instruction. GEOFENCE: kamino `borrow` is refused 451 `region_restricted` from a restricted region or an undeterminable one; `repay` is never gated, so a debt can always be reduced. Meteora: a deposit returns `createdPosition` — save it and pass it back as `positionAddress` to withdraw, since a fresh position takes a minute to appear to the owner scan; a deposit's liquidity add is bounded at 1% slippage, and a withdraw is all-or-nothing — it removes the whole position, claims its fees and closes the account. marginfi: a wallet may own several marginfi accounts, so a withdraw should name the one the position row came from (`account`, from GET /defi/positions); without it the first account holding the bank is used, which is the wrong one when there are several.","x-scope":"defi","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"venue":{"type":"string","enum":["kamino","marginfi","meteora"]},"action":{"type":"string","enum":["deposit","withdraw","borrow","repay"],"description":"Per venue · meteora/marginfi: deposit | withdraw; kamino: deposit | withdraw | borrow | repay. Anything else is 400 bad_action. Default deposit."},"mint":{"type":"string","description":"kamino/marginfi · the asset mint (required for those venues)."},"poolAddress":{"type":"string","description":"meteora · the DLMM pool, from GET /defi/pools (required)."},"depositMint":{"type":"string","description":"meteora · which side of the pool is supplied (required · must be one of the pool's two mints)."},"positionAddress":{"type":"string","description":"meteora withdraw · the `createdPosition` returned by the deposit. Optional; without it the owner scan is used, which cannot see a position created in the last minute."},"market":{"type":"string","description":"kamino · lending market address (optional · defaults to the main market; a value that is not an address is silently replaced by the main market, not refused)."},"amountUi":{"type":"number","description":"Human amount (e.g. 0.5). Required for deposit and borrow, and for a kamino/marginfi withdraw or kamino repay unless `max` is true. IGNORED on a meteora withdraw, which always removes the whole position."},"max":{"type":"boolean","description":"kamino/marginfi withdraw, kamino repay · everything. A meteora withdraw is always everything."},"account":{"type":"string","description":"marginfi withdraw · the marginfi account the position row came from (`account` on a GET /defi/positions marginfi row). Optional; without it the first of the wallet's accounts holding the bank is used. Refused 400 bad_account when it is not an address, and 400 bad_request when it is not one of the wallet's marginfi accounts."}},"required":["venue","action"],"additionalProperties":false,"allOf":[{"if":{"properties":{"venue":{"const":"meteora"}},"required":["venue"]},"then":{"properties":{"action":{"enum":["deposit","withdraw"]}}}},{"if":{"properties":{"venue":{"const":"marginfi"}},"required":["venue"]},"then":{"properties":{"action":{"enum":["deposit","withdraw"]}}}},{"if":{"properties":{"venue":{"const":"kamino"}},"required":["venue"]},"then":{"properties":{"action":{"enum":["deposit","withdraw","borrow","repay"]}}}}]},"examples":{"kaminoDeposit":{"summary":"Lend 5 USDC on Kamino","value":{"venue":"kamino","action":"deposit","mint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amountUi":5}},"marginfiWithdrawAll":{"summary":"Withdraw everything from marginfi · replace the placeholder with the account from the GET /defi/positions row before sending","value":{"venue":"marginfi","action":"withdraw","mint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","max":true,"account":"<account from the GET /defi/positions row>"}},"meteoraDeposit":{"summary":"Add one-sided SOL liquidity to a DLMM pool · replace the placeholder with a poolAddress from GET /defi/pools before sending","value":{"venue":"meteora","action":"deposit","poolAddress":"<pool address>","depositMint":"So11111111111111111111111111111111111111112","amountUi":0.1}}}}}},"responses":{"200":{"description":"Unsigned lending/liquidity transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefiBuild"}}}},"400":{"description":"bad_venue, bad_action (not one of the venue's actions), bad_pool, bad_mint, bad_position, bad_pubkey, bad_amount, bad_account (marginfi · `account` is not an address), bad_request (the message says which · marginfi: `account` is not one of the wallet's accounts, no bank takes deposits in that mint, or no position to withdraw; meteora: depositMint is not a side of the pool, no position at positionAddress, no position found for the wallet in the pool yet, the position spans more bins than one transaction can exit, or a poolAddress that is a valid address but not one of GET /defi/pools; kamino: a mint with no reserve; any venue: an amount that rounds to zero base units, or to zero after the fee), or bad_json"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"413":{"description":"payload_too_large · the body is over 4,096 bytes."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"451":{"description":"region_restricted · kamino `borrow` only, from a restricted region or an undeterminable one. Repay, withdraw and deposit are never gated. Wallets the operator designates for testing and operation (including its own) are judged on identity rather than country."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"no_treasury · this deployment has no fee treasury configured, so a fee-bearing deposit is refused rather than built unmonetised. On any endpoint: store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/tx/{signature}":{"get":{"operationId":"txStatus","summary":"Where a broadcast transaction stands · the only correct way to follow up an /execute.","description":"Poll THIS after /execute rather than building again: a rebuild carries a fresh blockhash and a different signature, so both can land — two swaps, two fees, one intended. `status` is `processed`, `confirmed` or `finalized` as the network advances, `failed` when the transaction landed and errored (`err` carries the program error), and `unknown` while the signature has not been seen — which is also what a dropped transaction looks like, so treat `unknown` past the blockhash's life as dead and only then build again. Searches transaction history, so an older signature still answers. RATE LANE: because any scope admits it, this call is metered in the TRADING lane (120/min, 20,000/day), not the read lane — a 2-second poll spends 30 trading calls a minute, and answers for one signature coalesce for 2 s per instance, so polling faster than that buys nothing. `blockHeight` is what makes `pending` terminable: every build — /swap/build, /orders create, /perps/build, /defi/build, and /lev (per leg on a loop) — returns the `lastValidBlockHeight` to compare it with. An order cancel does not; for that one, treat `unknown` as dead only once the blockhash has expired (roughly 60-90 s after the build).","x-scope":"read, trade, perps or defi · any one, so a key that can execute can always resolve what it executed","parameters":[{"name":"signature","in":"path","required":true,"schema":{"type":"string"},"description":"The base58 transaction signature /execute returned."}],"responses":{"200":{"description":"Signature status","content":{"application/json":{"schema":{"type":"object","properties":{"signature":{"type":"string","description":"The signature asked about."},"status":{"type":"string","enum":["processed","confirmed","finalized","failed","unknown"],"description":"Network view of the signature."},"slot":{"type":["integer","null"],"description":"Slot it landed in, or null."},"err":{"type":["object","string","null"],"description":"The program error when status is failed, else null."},"blockHeight":{"type":["integer","null"],"description":"Current confirmed block height, for judging a blockhash's remaining life."}},"required":["signature","status","slot","err","blockHeight"],"additionalProperties":false}}}},"400":{"description":"bad_signature · not a base58 transaction signature"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"forbidden · the key lacks the scope this endpoint accepts. Not metered."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}},"/execute":{"post":{"operationId":"execute","summary":"Broadcast a SIGNED transaction · sent, rebroadcast every ~2 s, watched for up to 25 s, then answered confirmed, finalized, failed or pending. Refuses any tx whose fee payer is not the key's wallet, or whose signature does not verify.","description":"The fee payer's ed25519 signature is verified over the exact bytes submitted BEFORE anything is sent — a mis-signed or stale-signed payload is refused 400 `bad_signature` in milliseconds, never left to poll for 25 seconds and come back `pending`. Any trading-lane scope admits the caller: a perps-only or defi-only key broadcasts what it built through /perps/build or /defi/build the same way a trade key broadcasts a swap.","x-scope":"trade, perps or defi · any one","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"txBase64":{"type":"string","description":"The base64 transaction, signed by the wallet."}},"required":["txBase64"],"additionalProperties":false}}}},"responses":{"200":{"description":"Broadcast result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteResult"}}}},"400":{"description":"bad_json, bad_tx (missing, not a transaction, or a txBase64 string over 6,000 characters), too_large (over the 1232-byte packet · can never land), unsigned (the fee payer's slot is empty), unsigned_cosigner (another required signer's slot is still empty · the transaction can never land, so it is refused rather than polled), bad_signature (the slot holds bytes that do not verify over this message), or bad_blockhash (the signature is unknown to the cluster AND its blockhash has already expired · nothing was broadcast; rebuild from a fresh build)"},"401":{"description":"unauthorized · no key, a malformed one (`Bearer` is case-sensitive, followed by one space, `pzk_` and 64 hex characters), or an unknown or revoked key. A refusal is not metered."},"403":{"description":"wrong_signer · the transaction's fee payer is not the key's wallet; or forbidden · the key has none of trade, perps, defi"},"413":{"description":"payload_too_large · the body is over 8,192 bytes."},"429":{"description":"rate_limited · over a window: per key 300 reads/min and 100,000/day or 120 trading calls/min and 20,000/day (fixed windows from the first call), or 600 requests/min per IP. The message names which window tripped and a `Retry-After` header (seconds) says when it turns; a refusal is not metered."},"500":{"description":"internal · a failure on our side; the detail is in our log, never in the message. Retry once."},"503":{"description":"store_unavailable · the key store could not answer, so the key could not be verified. The key is NOT at fault: retry shortly, do not rotate it."},"504":{"description":"upstream_timeout · an upstream (router, RPC, venue) did not answer in time · retry shortly."}}}}},"x-agent":{"bootstrap":["GET /key · prove the key, learn its wallet, scopes and limits","GET /markets · mints + decimals for everything else","GET /balances · what the wallet holds, to size orders","POST /swap/quote · sanity-check pricing before any build","then build → sign locally → POST /execute"],"meteora":"A Meteora deposit response includes createdPosition — SAVE IT and pass it back as positionAddress to withdraw. Fresh positions take a minute to appear to the owner scan, so withdrawing without it right after a deposit is refused by name.","mints":{"SOL":"So11111111111111111111111111111111111111112","USDC":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","USDT":"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB","jitoSOL":"J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn"},"signing":"JS (@solana/web3.js v1): const tx = VersionedTransaction.deserialize(Buffer.from(txBase64, 'base64')); tx.sign([keypair]); const signed = Buffer.from(tx.serialize()).toString('base64'); then POST { txBase64: signed } to /execute. The transaction's fee payer is always the key's wallet.","keyGeneration":"https://www.prizm.trading/wallet","docs":"https://www.prizm.trading/developers","mcp":"https://www.prizm.trading/api/mcp","flow":"For any trade: call the relevant build endpoint → deserialize txBase64 → sign locally with the wallet key → POST the signed tx to /execute. Reads (markets, prices, positions) need only the key. A key can never move funds.","fees":"Every fee is a direct transfer to the PRIZM treasury inside the same transaction, so a transaction that fails pays nothing. Swaps: 0.85% CARVED OUT of amountBase (the swap runs on the remainder). Limit and DCA orders: 0.85% of the committed amount ADDED ON TOP at placement — size the wallet for amount × 1.0085 — and not refunded if the order never fills, expires or is cancelled. Perps: 0.10% of notional in SOL on an open (settled when the request lands, not returned if the keeper refunds the collateral), nothing on a close. DeFi: 0.33% carved out of a deposit, nothing on withdraw, borrow or repay. Leveraged spot (/lev): 0.33% of the fresh stake on an atomic open with no fee on its internal swap; close-atomic carries no platform fee at all; the loop path pays 0.33% on every deposit leg and 0.85% on every swap leg, and a loop close carries no lending fee but its swap legs (the collateral sold to repay, and any settleTo conversion) each carve the 0.85% swap fee — each leg reports its own feeBps. Promoted wallets pay nothing anywhere. The rates are the ones this deployment is configured with; the per-call fee figure on every quote and build — `fee` on swaps and orders, `feeLamports` on perps, `feeBps`/`feeUi` on DeFi, `plan.feeBps` and per-leg `feeBps` on /lev — is AUTHORITATIVE for the wallet in hand — trust it over the headline rates.","errors":{"shape":"Every error is { error: { code, message } } with a matching HTTP status · an unknown path (404), a wrong method (405) and an oversize body (413) included. Error answers are never cacheable (`cache-control: no-store`).","order":"Auth is checked BEFORE argument validation · with a bad key you get 401, never field feedback. Call GET /key first to prove the key, then debug arguments.","codes":{"401 unauthorized":"Missing or invalid API key.","403 forbidden":"The key lacks the scope this endpoint needs (read/trade/perps/defi). /execute accepts any one of trade, perps or defi.","403 wrong_signer":"execute: the transaction's fee payer is not the key's wallet.","400 unsigned":"execute: the transaction is not signed.","400 bad_signature":"execute: the fee payer's signature does not verify over the submitted bytes · sign exactly what you submit, and submit exactly what you signed.","400 bad_json":"The body was not valid JSON, or not a JSON object.","400 bad_*":"A request field was missing or malformed (bad_mint, bad_amount, bad_action, bad_side, …). Enums are strict: an unrecognised action or side is refused by name, never coerced.","404 not_found":"No such endpoint under /api/v1 · every unknown path answers this envelope, for every method.","405 method_not_allowed":"The endpoint exists but not for that method · the `Allow` header names the ones it serves.","409 *":"/lev only · a state the caller can change: position_open, supply_present, no_position, ambiguous_position, too_large, insufficient_collateral, fee_on_predeposit.","413 payload_too_large":"The body is over the endpoint's cap (2–8 KB depending on the endpoint) · the message names the cap.","422 tx_too_large":"swap/build, orders (create) and /lev loop swap legs: the transaction will not carry the fee inside the 1232-byte packet · a smaller amount or a simpler pair.","428 setup_required":"/lev: sign the one-time { action: 'setup' } transaction first.","429 rate_limited":"Over a limit · per key: 300 reads/min and 100,000 reads/day, 120 trading calls/min and 20,000/day (fixed windows from the first call; the daily window is 24 hours, not a calendar day); per IP: 600/min; a /lev loop open also draws one unit per leg from a 240-legs/min per-key budget. Reads and trading calls are counted SEPARATELY, per minute as per day — a read-heavy poll never spends the trading budget. GET /key counts as a read; GET /tx/{signature} counts as a TRADING call (any scope admits it); every /orders action, including list, and /swap/quote count as trading. The message names which window tripped. Only an accepted call is metered as a call; a 401, 403 or 429 is not.","451 region_restricted":"Unavailable in the caller's region, or the region could not be determined (the gate fails closed). Applies ONLY to perps opens, leveraged opens (/lev open, open-atomic) and Kamino borrows. Perps closes, leveraged closes and repays are never gated. Wallets the operator designates for platform testing and operation (including its own administrative wallet) are judged on the key's wallet identity rather than the request's country.","500 internal":"Something failed on our side · the detail is in our log, never in the message. Retry once; if it persists, it is ours to fix.","502 upstream":"A routing/upstream rejection (e.g. below a minimum order size) — the message carries the reason.","503 unavailable":"perps/build: perpetuals are not enabled on this deployment · or, generically, a backing service could not answer; retry shortly.","503 store_unavailable":"The key store could not answer, so the key could not be verified · the key is NOT at fault. Retry shortly; do not rotate it.","503 unconfigured":"/prices: this deployment has no price backbone configured.","503 no_treasury":"swap/build, orders (create), perps/build (open), defi/build (deposit) and /lev open-atomic: this deployment has no fee treasury configured, so a fee-bearing build is refused rather than built with a fee nobody collects. Promoted wallets are unaffected.","503 no_rates / no_positions / no_store":"/defi/rates, /defi/positions, /feeds: the venues or the store behind the read are unreachable right now.","504 upstream_timeout":"An upstream (router, RPC, venue) did not answer in time · retry shortly."}},"cors":"None on /api/v1 · the surface is server-to-server: it sends no Access-Control-* headers, so a browser page cannot call it directly and a key never has to reach a browser. The MCP endpoint (/api/mcp) allows origin `*` so browser-based MCP clients can connect.","units":"Swaps and orders take base-unit integer strings (amountBase / makingAmountBase / …); DeFi takes human numbers (amountUi); perps takes collateralSol and sizeUsd as numbers. Token decimals come from /markets. perps/build accepts priceUsd but will use the live mark if you omit it."}}