{"openapi":"3.1.0","info":{"title":"Tplus Market Data Service API","description":"Public read-only market data API for Tplus. Endpoints are unauthenticated and expose finalized public trades, market-depth snapshots, candlestick history, 24h tickers, and matching WebSocket streams. WebSocket routes send a welcome frame first, then stream the documented payload type. Per-user trade history is served here for users whose data is exported by their OMS; account, order, balance, and position APIs remain on the Order Management System.","license":{"name":""},"version":"0.1.0"},"paths":{"/auth":{"post":{"tags":["Authentication"],"summary":"Exchange a signed nonce for a bearer token","description":"Verifies the Ed25519 signature over the nonce issued by `GET /nonce/{user}` and, on success, returns a bearer token valid about 24 hours. The nonce is single-use and consumed only when the signature is valid. Rate limited per client IP.","operationId":"document_auth","requestBody":{"description":"Signed nonce payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthRequestBody"}}},"required":true},"responses":{"200":{"description":"Authentication successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTokenResponse"}}}},"400":{"description":"Invalid signature or nonce","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}},"/health":{"get":{"tags":["Health"],"summary":"Market data service readiness check","description":"Returns `{\"status\":\"healthy\"}` with HTTP 200 once the market-data router is ready, or `{\"status\":\"warming up\"}` with HTTP 503 while startup is still in progress. No authentication required.","operationId":"document_health","responses":{"200":{"description":"Service is ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"503":{"description":"Service is warming up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/klines/diff/{asset_id}":{"get":{"tags":["WebSockets"],"summary":"Stream kline updates","description":"WebSocket stream of finalized candlestick/kline updates for one market. Bucket width comes from `timebar_bucket_size_ms`; the checked-in MDS config sets it to 1000 ms. The first text frame is `WsWelcome` for channel `klines`; later frames are `Timebar` payloads emitted when a bucket closes and flushes. Re-fetch REST kline snapshots after reconnecting or when client-side state may be stale. No authentication required.","operationId":"document_klines_stream","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}}],"responses":{"101":{"description":"WebSocket upgrade established for kline updates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Timebar"}}}}}}},"/klines/{asset_id}":{"get":{"tags":["Market Data"],"summary":"Get kline timebars","description":"Returns finalized candlestick timebars in reverse chronological order, starting from `end_timestamp_ns` when provided or now by default. Bucket width comes from `timebar_bucket_size_ms`; the checked-in MDS config sets it to 1000 ms. At that width, the in-memory retention is up to 7 days of finalized buckets per market, evicting oldest buckets first. The current open bucket is not returned by this REST endpoint or the kline WebSocket stream until it closes and flushes. `page` is zero-based, `limit` defaults to 100 and is clamped to 1..=18000. The response carries `total_pages`/`has_next_page` so clients can page through all bars.","operationId":"document_get_klines_snapshot","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}},{"name":"end_timestamp_ns","in":"query","description":"Upper-bound close timestamp in nanoseconds (default: now)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 18000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Timebars returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_Timebar"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}},"/marketdepth/diff/{asset_id}":{"get":{"tags":["WebSockets"],"summary":"Stream market depth updates","description":"WebSocket stream of incremental market-depth updates for one market. Fetch `GET /marketdepth/{asset_id}` first, then subscribe to this stream. The first text frame is `WsWelcome` for channel `depth`; later frames carry changed bid and ask levels as `[price, quantity]` pairs. Quantity `\"0\"` means that level was removed. Re-fetch the REST snapshot on reconnect or when sequence numbers indicate a gap. No authentication required.","operationId":"document_depth_stream","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}}],"responses":{"101":{"description":"WebSocket upgrade established","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonPriceLevelUpdated"}}}}}}},"/marketdepth/{asset_id}":{"get":{"tags":["Market Data"],"summary":"Get market depth snapshot","description":"Returns the current order-book depth snapshot for one market. Bids are sorted highest price first, asks are sorted lowest price first, and `sequence_number` can be compared with depth-diff stream updates to detect missed data.","operationId":"document_get_marketdepth_snapshot","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current order book depth","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketDepthSnapshot"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}},"/nonce/{user}":{"get":{"tags":["Authentication"],"summary":"Get auth nonce","description":"Returns a random nonce bound to `user`, valid about 5 minutes. Sign the returned `value` string with the user's Ed25519 private key and submit it to `POST /auth` to mint a bearer token. `ce_nonce` is always `0` on the market data service (no multisig actions). Rate limited per client IP.","operationId":"document_nonce","parameters":[{"name":"user","in":"path","description":"Hex-encoded Ed25519 public key (with or without 0x prefix)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Nonce generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NonceResponse"}}}},"400":{"description":"Invalid user id format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}},"/orders/user/{user_id}":{"get":{"tags":["Orders"],"summary":"List user orders","description":"Returns the authenticated user's **full order history**: every order, filled and cancelled included, newest first (sorted by last-update timestamp, then order id, descending). Use this for history views, audits, reconciliation and reporting. For **open orders** (live, working order state and in-flight quantities) call the OMS `/orders/user` endpoint instead. This copy is exported from the OMS and eventually consistent; a user with no exported order history returns an empty page. `page` is zero-based, `limit` defaults to 100 and is capped at 1000. Requires a valid MDS-minted bearer token; the `{user_id}` path segment must match the `User-Id` header.","operationId":"document_get_user_orders","parameters":[{"name":"user_id","in":"path","description":"Hex-encoded user public key (case-insensitive); must match the authenticated caller","required":true,"schema":{"type":"string"}},{"name":"User-Id","in":"header","description":"Hex-encoded user public key (case-insensitive) identifying the caller","required":true,"schema":{"type":"string"}},{"name":"sub_account","in":"query","description":"Restrict to a single sub-account by account index (default: all sub-accounts)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"start_time","in":"query","description":"Only orders created at or after this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"end_time","in":"query","description":"Only orders created at or before this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"side","in":"query","description":"Restrict to `buy` or `sell` orders","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Restrict to one status: `pending`, `open`, `partial`, `cancelled`, `closed` (cancelled after a partial fill) or `completed` (fully filled)","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Orders returned successfully (empty page if the user has no exported orders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOrdersPageResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Bearer token missing, invalid, or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"The `{user_id}` path segment does not match the authenticated caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"503":{"description":"Service warming up, or auth unavailable (OMS unreachable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"bearer_token":[]}]}},"/orders/user/{user_id}/{asset_id}":{"get":{"tags":["Orders"],"summary":"List user orders for a market","description":"Same as `/orders/user/{user_id}`, restricted to one market.","operationId":"document_get_user_orders_for_asset","parameters":[{"name":"user_id","in":"path","description":"Hex-encoded user public key (case-insensitive); must match the authenticated caller","required":true,"schema":{"type":"string"}},{"name":"User-Id","in":"header","description":"Hex-encoded user public key (case-insensitive) identifying the caller","required":true,"schema":{"type":"string"}},{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}},{"name":"sub_account","in":"query","description":"Restrict to a single sub-account by account index (default: all sub-accounts)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"start_time","in":"query","description":"Only orders created at or after this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"end_time","in":"query","description":"Only orders created at or before this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"side","in":"query","description":"Restrict to `buy` or `sell` orders","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Restrict to one status: `pending`, `open`, `partial`, `cancelled`, `closed` (cancelled after a partial fill) or `completed` (fully filled)","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Orders returned successfully (empty page if the user has no exported orders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOrdersPageResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Bearer token missing, invalid, or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"The `{user_id}` path segment does not match the authenticated caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"503":{"description":"Service warming up, or auth unavailable (OMS unreachable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"bearer_token":[]}]}},"/ticker/ws/{asset_id}":{"get":{"tags":["WebSockets"],"summary":"Stream ticker updates for a market","description":"WebSocket stream of 24h ticker updates for one market. Price changes are aggregated into roughly 1-second windows, and idle markets may not emit messages. The first text frame is `WsWelcome` for channel `ticker`; later frames are `TickerResponse` payloads. Re-fetch REST ticker snapshots after reconnecting. No authentication required.","operationId":"document_ticker_ws","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}}],"responses":{"101":{"description":"WebSocket upgrade established","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickerResponse"}}}}}}},"/ticker/{asset_id}":{"get":{"tags":["Market Data"],"summary":"Get 24h ticker for a market","description":"Returns the latest ticker for one market, including last price, mark price, rolling 24h volume/high/low, and rolling 24h price change when enough price history is available.","operationId":"document_get_ticker","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ticker returned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickerResponse"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}},"/tickers":{"get":{"tags":["Market Data"],"summary":"Get 24h tickers for all markets","description":"Returns one ticker per known market. Tickers include last price, mark price, rolling 24h volume/high/low, and rolling 24h price change when enough price history is available.","operationId":"document_get_tickers","responses":{"200":{"description":"Tickers returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TickerResponse"}}}}}}}},"/tickers/ws":{"get":{"tags":["WebSockets"],"summary":"Stream ticker updates for all markets","description":"WebSocket stream of 24h ticker updates for all markets. Price changes are aggregated into roughly 1-second windows; each message is an array containing the markets that changed during the window. The first text frame is `WsWelcome` for channel `tickers`; later frames are arrays of `TickerResponse`. Re-fetch REST ticker snapshots after reconnecting. No authentication required.","operationId":"document_tickers_ws","responses":{"101":{"description":"WebSocket upgrade established","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TickerResponse"}}}}}}}},"/trades":{"get":{"tags":["Trades"],"summary":"List all public trades","description":"Returns confirmed public trades across all markets in reverse chronological order from the in-memory REST cache. `page` is zero-based, `limit` defaults to 100, and the service caps it at 1000. The default service cache retains the newest 100000 trades FIFO; WebSocket delivery is live and not capped by that REST cache.\n\nThe same route also accepts WebSocket upgrades. With WebSocket upgrade headers, the response is `101`; the first text frame is `WsWelcome` for channel `finalized-trades`, followed by finalized public `Trade` messages across all markets. These routes receive only finalized trades from OMS, but the payload reuses the shared `TradeStatus` enum and should be `Confirmed` here. No authentication required.","operationId":"document_get_all_trades","parameters":[{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"101":{"description":"WebSocket upgrade established for finalized public trades; first frame is `WsWelcome`, then `Trade` messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trade"}}}},"200":{"description":"Trades returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Trade"}}}}}}}},"/trades/user/{user_id}":{"get":{"tags":["Trades"],"summary":"List user trades","description":"Returns the authenticated user's trades across all markets, newest first (sorted by execution timestamp, then trade id, descending). A user with no exported trade history returns an empty page. `page` is zero-based, `limit` defaults to 100 and is capped at 1000. Requires a valid MDS-minted bearer token; the `{user_id}` path segment must match the `User-Id` header.","operationId":"document_get_user_trades","parameters":[{"name":"user_id","in":"path","description":"Hex-encoded user public key (case-insensitive); must match the authenticated caller","required":true,"schema":{"type":"string"}},{"name":"User-Id","in":"header","description":"Hex-encoded user public key (case-insensitive) identifying the caller","required":true,"schema":{"type":"string"}},{"name":"sub_account","in":"query","description":"Restrict to a single sub-account by account index (default: all sub-accounts)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"start_time","in":"query","description":"Only trades executed at or after this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"end_time","in":"query","description":"Only trades executed at or before this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"side","in":"query","description":"Restrict to trades where the user was the buyer (`buy`) or the seller (`sell`)","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Restrict to one status: `pending`, `confirmed` or `rollbacked`","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Trades returned successfully (empty page if the user has no exported trades)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserTradesPageResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Bearer token missing, invalid, or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"The `{user_id}` path segment does not match the authenticated caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"503":{"description":"Service warming up, or auth unavailable (OMS unreachable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"bearer_token":[]}]}},"/trades/user/{user_id}/{asset_id}":{"get":{"tags":["Trades"],"summary":"List user trades for a market","description":"Same as `/trades/user/{user_id}`, restricted to one market.","operationId":"document_get_user_trades_for_asset","parameters":[{"name":"user_id","in":"path","description":"Hex-encoded user public key (case-insensitive); must match the authenticated caller","required":true,"schema":{"type":"string"}},{"name":"User-Id","in":"header","description":"Hex-encoded user public key (case-insensitive) identifying the caller","required":true,"schema":{"type":"string"}},{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}},{"name":"sub_account","in":"query","description":"Restrict to a single sub-account by account index (default: all sub-accounts)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"start_time","in":"query","description":"Only trades executed at or after this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"end_time","in":"query","description":"Only trades executed at or before this time, in nanoseconds since the Unix epoch (inclusive)","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"side","in":"query","description":"Restrict to trades where the user was the buyer (`buy`) or the seller (`sell`)","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Restrict to one status: `pending`, `confirmed` or `rollbacked`","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Trades returned successfully (empty page if the user has no exported trades)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserTradesPageResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Bearer token missing, invalid, or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"The `{user_id}` path segment does not match the authenticated caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"503":{"description":"Service warming up, or auth unavailable (OMS unreachable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"bearer_token":[]}]}},"/trades/{asset_id}":{"get":{"tags":["Trades"],"summary":"List public trades for a market","description":"Returns confirmed public trades for one market in reverse chronological order from the in-memory REST cache. `page` is zero-based, `limit` defaults to 100, and the service caps it at 1000. The default service cache retains the newest 100000 trades FIFO across markets; filtering is applied when this endpoint is read.\n\nThe same route also accepts WebSocket upgrades. With WebSocket upgrade headers, the response is `101`; the first text frame is `WsWelcome` for channel `finalized-trades`, followed by finalized public `Trade` messages for this market. These routes receive only finalized trades from OMS, but the payload reuses the shared `TradeStatus` enum and should be `Confirmed` here. No authentication required.","operationId":"document_get_trades_for_asset","parameters":[{"name":"asset_id","in":"path","description":"Stringified AssetIdentifier: numeric index like `0`, or address form `<32-byte hex address>@<1-byte routing id><8-byte vm id>`","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page number (default: 0)","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","description":"Items per page, max 1000 (default: 100)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"101":{"description":"WebSocket upgrade established for finalized market trades; first frame is `WsWelcome`, then `Trade` messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trade"}}}},"200":{"description":"Trades returned successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Trade"}}}}}}}}},"components":{"schemas":{"AccountIndex":{"type":"integer","format":"int64","minimum":0},"AdditionalSigner":{"type":"object","required":["signer","signature"],"properties":{"signature":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}},"signer":{"$ref":"#/components/schemas/SignerKey"}}},"ApiErrorBody":{"type":"object","description":"Inner error body - same shape as the WS v1 `WsErrorOut`.","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code (`SCREAMING_SNAKE`)."},"details":{"description":"Optional structured details (e.g. order_id, field names, CE error text, and the\nsolvency `rejection` breakdown for InsufficientMargin/InsufficientInventory)."},"message":{"type":"string","description":"Human-readable error description."},"retryable":{"type":["boolean","null"],"description":"Whether the client should retry the request."},"span_id":{"type":["string","null"],"description":"Span id of the request, for correlating with service logs."},"trace_id":{"type":["string","null"],"description":"Trace id of the request, for correlating with service logs."}}},"ApiErrorResponse":{"type":"object","description":"Top-level API error response envelope.\n\n```json\n{\n  \"error\": {\n    \"code\": \"INSUFFICIENT_MARGIN\",\n    \"message\": \"Insufficient margin for order\",\n    \"details\": {\"order_id\": \"abc123\"},\n    \"retryable\": false\n  }\n}\n```","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/ApiErrorBody","description":"The error payload (code, message, optional details, retry hint)."}}},"AuthRequestBody":{"type":"object","description":"Request body for `POST /auth`.","required":["user_id","nonce","signature","additional_signers"],"properties":{"additional_signers":{"type":"array","items":{"$ref":"#/components/schemas/AdditionalSigner"},"default":"Vec::new","description":"Optional multisig co-signatures over the same nonce payload."},"nonce":{"type":"string","example":"a1b2c3d4e5f6...","description":"Exact nonce string returned by `GET /nonce/{user}`."},"signature":{"type":"array","items":{"type":"integer","format":"int32","minimum":0},"description":"Signature bytes as a JSON array of integers over the raw UTF-8 nonce string."},"user_id":{"type":"string","example":"0xeb886a56f9f0efa64432678cebf1270e9314a758e6eb697a606202a451e3e82e","description":"Hex-encoded user public key."}}},"AuthTokenResponse":{"type":"object","description":"Bearer token from `POST /auth`.","required":["token","expiry_ns"],"properties":{"expiry_ns":{"type":"integer","format":"int64","example":1710086400000000000,"minimum":0,"description":"Token expiry (ns since the Unix epoch); valid about 24 hours."},"token":{"type":"string","example":"9f8e7d6c5b4a...","description":"Bearer token to send as the `Authorization: Bearer <token>` header."}}},"BookLevel":{"type":"object","description":"A single book level: price and total size resting at that price.","required":["price","size"],"properties":{"price":{"type":"string","example":"105399.75"},"size":{"type":"string","example":"2.5"}}},"ErrorCode":{"type":"string","description":"Generic, cross-service error codes. Services use these for standard HTTP-shaped errors\nand define their own enum (implementing [`ErrorCodeExt`]) for domain-specific ones.","enum":["INVALID_JSON","INVALID_QUERY","INVALID_REQUEST","INVALID_USER_ID","UNAUTHORIZED","WRONG_AUTHORIZATION","NOT_FOUND","USER_NOT_FOUND","MARKET_NOT_FOUND","ASSET_NOT_FOUND","METHOD_NOT_ALLOWED","RATE_LIMITED","TIMEOUT","INTERNAL_ERROR"]},"HealthResponse":{"type":"object","required":["status"],"properties":{"status":{"type":"string","description":"Service readiness state.","example":"healthy"}}},"JsonBookOrder":{"type":"object","required":["order_id","base_asset","account_index","is_spot","side","limit_price","quantity","amount","max_sellable_amount","max_sellable_quantity","confirmed_filled_quantity","pending_filled_quantity","confirmed_filled_amount","pending_filled_amount","confirmed_trading_fees_amount","pending_trading_fees_amount","timestamp_ns","in_flight","canceled","status","trigger_above_price","trigger_below_price","trigger_enabled_quantity","last_update_timestamp_ns","is_immediate_or_cancel","is_fill_or_kill","is_liquidation","is_auto_deleverage","is_reduce_only","max_trading_fees_rate"],"properties":{"account_index":{"$ref":"#/components/schemas/AccountIndex"},"amount":{"type":"string","example":"1000"},"base_asset":{"type":"string","example":"200"},"canceled":{"type":"boolean"},"confirmed_filled_amount":{"type":"string","example":"21000"},"confirmed_filled_quantity":{"type":"string","example":"200"},"confirmed_trading_fees_amount":{"type":"string","example":"210"},"good_until_timestamp_ns":{"type":["integer","null"],"format":"int64","example":null,"minimum":0},"in_flight":{"type":"boolean"},"is_auto_deleverage":{"type":"boolean"},"is_fill_or_kill":{"type":"boolean"},"is_immediate_or_cancel":{"type":"boolean"},"is_liquidation":{"type":"boolean"},"is_reduce_only":{"type":"boolean"},"is_spot":{"type":"boolean"},"last_update_timestamp_ns":{"type":"integer","format":"int64","example":"1750146943779456128","minimum":0},"limit_price":{"type":"string","example":"105420.25"},"max_sellable_amount":{"type":"string","example":"1000"},"max_sellable_quantity":{"type":"string","example":"1000"},"max_trading_fees_rate":{"type":"integer","format":"int64","example":"5000","minimum":0},"order_id":{"type":"string","example":"rt6G7V8gRAG4p7lfidkeUw=="},"parent_id":{"type":["string","null"],"example":"rt6G7V8gRAG4p7lfidkeUw=="},"pending_filled_amount":{"type":"string","example":"42000"},"pending_filled_quantity":{"type":"string","example":"400"},"pending_trading_fees_amount":{"type":"string","example":"420"},"quantity":{"type":"string","example":"1000"},"side":{"$ref":"#/components/schemas/OrderSide"},"status":{"$ref":"#/components/schemas/ViewOrderStatus"},"timestamp_ns":{"type":"integer","format":"int64","example":"1750146943779456128","minimum":0},"trigger_above_price":{"type":"string"},"trigger_below_price":{"type":"string"},"trigger_enabled_quantity":{"type":"string"},"trigger_touched":{"type":["boolean","null"]}}},"JsonPriceLevelUpdated":{"type":"object","required":["bids","asks","sequence_number"],"properties":{"asks":{"type":"array","items":{"type":"array","items":false,"prefixItems":[{"type":"string"},{"type":"string"}]},"description":"Changed ask levels as `[price, quantity]` string pairs, lowest price first. Quantity `\"0\"` removes the level.","example":"[[\"0.00500000\",\"0.00100000\"]]"},"bids":{"type":"array","items":{"type":"array","items":false,"prefixItems":[{"type":"string"},{"type":"string"}]},"description":"Changed bid levels as `[price, quantity]` string pairs, highest price first. Quantity `\"0\"` removes the level.","example":"[[\"0.00400000\",\"0.00100000\"], [\"0.00300000\",\"0.00200000\"]]"},"sequence_number":{"type":"integer","format":"int64","minimum":0,"description":"Monotonic sequence number for ordering depth updates and detecting gaps."}},"description":"Incremental market-depth update. Levels are `[price, quantity]` string pairs."},"MarketDepthSnapshot":{"type":"object","required":["asks","bids","sequence_number"],"properties":{"asks":{"type":"array","items":{"type":"array","items":false,"prefixItems":[{"type":"string"},{"type":"string"}]},"description":"Ask levels as `[price, quantity]` string pairs, lowest price first.","example":"[[\"0.00500000\",\"0.00100000\"]]"},"bids":{"type":"array","items":{"type":"array","items":false,"prefixItems":[{"type":"string"},{"type":"string"}]},"description":"Bid levels as `[price, quantity]` string pairs, highest price first.","example":"[[\"0.00400000\",\"0.00100000\"], [\"0.00300000\",\"0.00200000\"]]"},"sequence_number":{"type":"integer","format":"int64","minimum":0,"description":"Monotonic sequence number for ordering depth updates and detecting gaps."}},"description":"Full order-book depth snapshot for one market."},"NonceResponse":{"type":"object","description":"Nonce response from `GET /nonce/{user_id}`.","required":["value","expiry_ns","ce_nonce"],"properties":{"ce_nonce":{"type":"integer","format":"int64","example":2,"minimum":0,"description":"Always `0` on the market data service; retained for wire compatibility with the OMS nonce response."},"expiry_ns":{"type":"integer","format":"int64","example":1710000300000000000,"minimum":0,"description":"Nonce expiry (ns since the Unix epoch); valid about 5 minutes."},"value":{"type":"string","example":"a1b2c3d4e5f6...","description":"Random nonce to sign (Ed25519) and submit to `POST /auth`."}}},"OrderSide":{"type":"string","enum":["Buy","Sell"]},"PageMeta":{"type":"object","description":"Pagination metadata; `#[serde(flatten)]` it next to a response's `items`/`total_*` fields.","required":["page","limit","total_pages","cursor_size","has_next_page"],"properties":{"cursor_size":{"type":"integer","example":100,"minimum":0},"has_next_page":{"type":"boolean","example":true},"limit":{"type":"integer","example":100,"minimum":0},"next_page":{"type":["integer","null"],"example":1,"minimum":0},"page":{"type":"integer","example":0,"minimum":0},"total_pages":{"type":"integer","example":3,"minimum":0}}},"Page_Timebar":{"allOf":[{"$ref":"#/components/schemas/PageMeta"},{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object","required":["open","close","low","high","volume","open_timestamp_ns","close_timestamp_ns"],"properties":{"close":{"type":"string","example":"105180.50"},"close_timestamp_ns":{"type":"integer","format":"int64","description":"Timebar close timestamp in nanoseconds","example":"1750146944000000000","minimum":0},"high":{"type":"string","example":"105180.50"},"low":{"type":"string","example":"105040.00"},"open":{"type":"string","example":"105040.25"},"open_timestamp_ns":{"type":"integer","format":"int64","description":"Timebar open timestamp in nanoseconds","example":"1750146943000000000","minimum":0},"volume":{"type":"string","example":"45500.00"}}}}}}],"description":"Standard paged response: `items` plus flattened [`PageMeta`]."},"RollbackReason":{"type":"string","description":"Reason for rolling back an order when the clearing engine marks it at fault.\nOnly exposed to the user whose order was at fault (counterparty does not receive this).","enum":["CounterpartyAtFault","Insolvent","PricingIssue","BreachTotalOiCap","BreachSpotMarginOiCap","BreachCollateralCap","InvalidInventory","WrongAccountType","WrongSignature","InvalidOrderConditions","Overfilled","InvalidRelativeTime","NotReduceOnly","InvalidLiquidation","ProcessingError"]},"SignerKey":{"oneOf":[{"type":"object","required":["Ed25519"],"properties":{"Ed25519":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},{"type":"object","description":"secp256k1 pubkey (e.g EVM wallets).","required":["Secp256k1"],"properties":{"Secp256k1":{"type":"array","items":{"type":"integer","format":"int32","minimum":0},"description":"secp256k1 pubkey (e.g EVM wallets)."}}},{"type":"object","description":"P-256/secp256r1 pubkey (raw payload).","required":["P256"],"properties":{"P256":{"type":"array","items":{"type":"integer","format":"int32","minimum":0},"description":"P-256/secp256r1 pubkey (raw payload)."}}},{"type":"object","description":"P-256/secp256r1 pubkey with WebAuthn ceremony.","required":["WebAuthn"],"properties":{"WebAuthn":{"type":"array","items":{"type":"integer","format":"int32","minimum":0},"description":"P-256/secp256r1 pubkey with WebAuthn ceremony."}}}],"description":"Signer identity with multicurve support."},"TickerResponse":{"type":"object","required":["asset_id","volume_24h","timestamp_ns"],"properties":{"asset_id":{"type":"string","example":"200","description":"Market asset id, as a stringified `AssetIdentifier`."},"best_ask":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BookLevel","description":"Lowest resting ask (price + size)."}]},"best_bid":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BookLevel","description":"Highest resting bid (price + size)."}]},"high_24h":{"type":["string","null"],"example":"106000.00","description":"Highest traded price over the trailing 24 hours, or `null` when unavailable."},"index_price":{"type":["string","null"],"description":"Oracle price.","example":"105360.00"},"last_price":{"type":["string","null"],"example":"105400.25","description":"Most recent traded price, or `null` when no trade price is available."},"low_24h":{"type":["string","null"],"example":"104000.00","description":"Lowest traded price over the trailing 24 hours, or `null` when unavailable."},"mark_price":{"type":["string","null"],"example":"105350.00","description":"Current mark price, or `null` when no mark is available."},"price_change_24h":{"type":["string","null"],"example":"400.25","description":"Absolute change from the earliest available 24h price to `last_price`, or `null` when unavailable."},"price_change_pct_24h":{"type":["string","null"],"example":"1.25","description":"Percentage change over the trailing 24h window, or `null` when unavailable."},"timestamp_ns":{"type":"integer","format":"int64","example":"1750146943779456128","minimum":0,"description":"Ticker computation timestamp in nanoseconds since the Unix epoch."},"volume_24h":{"type":"string","example":"1234.5678","description":"Quote-notional volume over the trailing 24 hours, computed from kline bucket volume plus the active unflushed bucket."}},"description":"Rolling 24h ticker for one market."},"Timebar":{"type":"object","required":["open","close","low","high","volume","open_timestamp_ns","close_timestamp_ns"],"properties":{"close":{"type":"string","example":"105180.50","description":"Last traded price in this bucket."},"close_timestamp_ns":{"type":"integer","format":"int64","description":"Bucket close timestamp in nanoseconds since the Unix epoch.","example":"1750146944000000000","minimum":0},"high":{"type":"string","example":"105180.50","description":"Highest traded price in this bucket."},"low":{"type":"string","example":"105040.00","description":"Lowest traded price in this bucket."},"open":{"type":"string","example":"105040.25","description":"First traded price in this bucket."},"open_timestamp_ns":{"type":"integer","format":"int64","description":"Bucket open timestamp in nanoseconds since the Unix epoch.","example":"1750146943000000000","minimum":0},"volume":{"type":"string","example":"45500.00","description":"Quote-notional volume traded during this bucket (`price * quantity`)."}},"description":"Finalized candlestick/kline bucket for one market. Bucket width comes from `timebar_bucket_size_ms`; the checked-in MDS config sets it to 1000 ms. At that width, the service retains up to 7 days per market in memory."},"Trade":{"type":"object","required":["asset_id","trade_id","price","quantity","timestamp_ns","buyer_is_maker","status","is_liquidation","is_auto_deleverage"],"properties":{"asset_id":{"type":"string","example":"200","description":"Asset traded, as a stringified `AssetIdentifier`."},"buyer_is_maker":{"type":"boolean","description":"`true` when the buyer was the resting maker order."},"is_auto_deleverage":{"type":"boolean","description":"True when the order was an auto-deleverage order (AVS backstop ADL)."},"is_liquidation":{"type":"boolean","description":"`true` when the taker order was a forced-liquidation order. The counter-party maker side is unchanged."},"price":{"type":"string","example":"105400.25","description":"Execution price in human-readable units."},"quantity":{"type":"string","example":"0.1500","description":"Executed quantity in human-readable base units."},"status":{"$ref":"#/components/schemas/TradeStatus","description":"Shared trade status enum. Finalized MDS trade routes should emit `Confirmed`."},"timestamp_ns":{"type":"integer","format":"int64","example":"1750146943779456128","minimum":0,"description":"Execution timestamp in nanoseconds since the Unix epoch."},"trade_id":{"type":"integer","format":"int64","minimum":0,"description":"Monotonic trade identifier for this market. MDS uses it to ignore stale latest-price updates."}},"description":"Public trade record for a finalized match. This is market-level history, not the authenticated user-trade view. MDS finalized-trade routes receive confirmed trades, but this schema reuses the shared `TradeStatus` enum."},"TradeStatus":{"type":"string","enum":["Pending","Confirmed","Rollbacked"],"description":"Shared settlement status enum: `Pending`, `Confirmed`, or `Rollbacked`. On MDS finalized-trade REST and WebSocket routes, trades are expected to be `Confirmed` even though the shared enum includes other states."},"UserOrdersPageResponse":{"allOf":[{"$ref":"#/components/schemas/PageMeta"},{"type":"object","required":["orders","total_orders"],"properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/JsonBookOrder"},"description":"The page of orders, newest first (sorted by last-update timestamp, then order id, descending)."},"total_orders":{"type":"integer","description":"Total number of orders matching the query across all pages.","example":42,"minimum":0}}}]},"UserTrade":{"type":"object","required":["asset_id","trade_id","order_id","price","quantity","timestamp_ns","is_maker","is_buyer","status","sub_account","trading_fee"],"properties":{"asset_id":{"type":"string","example":"200","description":"Asset traded, as a stringified `AssetIdentifier`."},"is_auto_deleverage":{"type":"boolean","description":"True when the order was an auto-deleverage order (AVS backstop ADL)."},"is_buyer":{"type":"boolean","description":"`true` if the user was the buyer."},"is_liquidation":{"type":"boolean","description":"True when the taker order was a forced-liquidation order."},"is_maker":{"type":"boolean","description":"`true` if the user's order was the resting maker."},"order_id":{"type":"string","example":"rt6G7V8gRAG4p7lfidkeUw==","description":"The user's order id that participated in this trade."},"price":{"type":"string","example":"105400.25","description":"Execution price in human-readable units."},"quantity":{"type":"string","example":"0.1","description":"Executed quantity in human-readable base units."},"rollback_reason":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RollbackReason","description":"Reason for rollback when status is Rollbacked; only present for the user whose order was at fault."}]},"status":{"$ref":"#/components/schemas/TradeStatus","description":"Settlement status of the trade."},"sub_account":{"type":"integer","format":"int64","description":"Sub-account the order traded against (`0` = spot, `1` = default margin, …).","example":1,"minimum":0},"timestamp_ns":{"type":"integer","format":"int64","example":"1750146943779456128","minimum":0,"description":"Execution timestamp (ns since the Unix epoch)."},"trade_id":{"type":"integer","format":"int64","minimum":0,"description":"Monotonic trade identifier for this market."},"trading_fee":{"type":"string","description":"Trading fee for this user on the fill. Positive = paid, negative = rebate (USD).","example":"0.42"}}},"UserTradesPageResponse":{"allOf":[{"$ref":"#/components/schemas/PageMeta"},{"type":"object","required":["trades","total_trades"],"properties":{"total_trades":{"type":"integer","description":"Total number of trades matching the query across all pages.","example":253,"minimum":0},"trades":{"type":"array","items":{"$ref":"#/components/schemas/UserTrade"},"description":"The page of trades, newest first (sorted by execution timestamp, then trade id, descending)."}}}]},"ViewOrderStatus":{"type":"string","enum":["Pending","Open","Partial","Cancelled","Closed","Completed"]},"WsChannelDoc":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Channel name.","example":"depth"}}},"WsWelcome":{"type":"object","required":["type","channels"],"properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/WsChannelDoc"},"description":"Subscribed channels confirmed by the server."},"errors":{"type":["string","null"],"description":"Optional connection-level error text."},"type":{"type":"string","description":"Message type. Market-data streams currently use `subscriptions`.","example":"subscriptions"}}}},"securitySchemes":{"bearer_token":{"type":"http","scheme":"bearer"}}},"tags":[{"name":"Trades","description":"Confirmed public trade history and finalized-trade streams"},{"name":"Market Data","description":"Read-only market snapshots for depth, klines, and 24h tickers"},{"name":"WebSockets","description":"Unauthenticated market-data streams. For trade streams that share a REST route, see the route description."},{"name":"Authentication","description":"Ed25519 nonce/bearer-token issuance. Data endpoints are currently unauthenticated; these routes mint tokens for endpoints gated in future revisions."},{"name":"Health","description":"Service health checks"}]}