For the complete documentation index, see llms.txt. This page is also available as Markdown.

Known Gaps

Gaps and inconsistencies in the API contract, flagged rather than filled.

This page is a list of things the contract does not say. The endpoint pages in the Reference section are generated from openapi.json and describe exactly what the contract declares — no more. Where the contract is silent, ambiguous, or internally inconsistent, that is recorded here instead of being papered over with plausible prose.

Read it before building against an operation whose behaviour you are inferring rather than reading.

Each item names the surface it belongs to. Nothing here is a roadmap commitment; these are observations about the contract as it stands.

Verified against spec 0.9.27. Every item below was re-checked against the contract rather than carried forward — one earlier entry claimed the contract had no client order id or idempotency key, which client_id and the 409 DuplicateClientId response have since made false, so it was removed. A gap list that is itself stale is worse than no gap list.

Trading

  • Cancel response bodies. DELETE /orders, DELETE /orders/{order_id}, and both /api/v1 twins document a 200 with a prose description ("Cancelled orders returned" / "Cancelled order returned") but no response schema. The body shape is not part of the contract.

  • Batch limits. No maximum batch length is declared for POST /orders/batch, and the contract does not say how a batch is weighted against the rate limit (one request, or one per element).

  • reduce_only. Declared as a boolean with no description. Its interaction with the conditional and trailing order types is unspecified.

  • PreviewResponse field semantics. The eight fields are typed but carry no descriptions in the contract.

  • Trigger direction. "Adverse" and "favorable" are not formally defined per side for the stop and take-profit families.

  • Time-in-force × conditional types. The contract does not state which time_in_force values are valid for the six conditional order types (for example PostOnly on a StopMarket).

  • Amendability. Beyond "liquidation orders are not amendable", the contract does not say whether conditional or trailing orders can be amended, or whether an amend may change time_in_force.

  • stop_price removal. Marked deprecated with no stated removal version or date.

  • Order.price on market orders. Order.price is typed as a non-nullable decimal string, while OrderHistoryEntry.price for the same concept is explicitly nullable ("null for market orders"). What Order.price carries for a market order is not stated.

Positions

  • leverage is permanently null today. The contract states it is "currently always null" with leverage_error: "margin_state_not_mirrored", and gives no target for populating it. Clients cannot read per-position leverage from this surface.

  • No market_id filter on closed positions. GET /positions/closed accepts only limit and cursor; there is no way to scope the query to a single market.

  • Retention window unstated. funding_paid is documented as "bounded by the funding history the indexer retains" and cursor pagination as bounded by "the retained window", but no retention period is given for either.

  • 429 missing on closed positions. GET /positions/closed and its /api/v1 twin declare only 200 and 401; the 429 response the other position operation declares is absent from the contract even though the same rate-limit layer applies.

  • Cross-margin only. margin_used is defined "under the engine's cross-margin model", and the contract notes isolated/custom margin allocations are not mirrored by the indexer — so an isolated-margin position's true allocation is not readable here.

  • No open-position count or total. The response is a bare array with no envelope and no pagination on GET /positions, so there is no documented bound on how many positions a single response may carry.

Account

  • direction is not enumerated. POST /account/margin declares no request schema at all. Its example shows "direction": "add", and the summary and 400 description imply removal is supported, but the contract never states the value for removal. Nor does it name the field types or mark any field required.

  • Three operations declare no schema. POST /account/deposit (request and response) and POST /account/margin (request and response) carry examples only. Clients generated from the contract get untyped bodies for these.

  • Two overlapping deposit paths. POST /account/deposit and POST /deposits both deposit collateral, return different shapes ({balance} vs DepositResponse), and the contract does not say which to prefer or how they differ operationally.

  • Status vocabularies disagree. Withdrawal.status is pending / settled / failed; FundsEntry.status is pending / confirmed / failed. The same lifecycle is named two ways.

  • GET /orders/{order_id} has no /api/v1 twin. A client standardising on the versioned surface must fall back to the gateway path for single-order lookup.

  • Rate-limit tier casing is inconsistent. RateLimitStatus.tier is documented lowercase (pro, marketmaker, unlimited); the 429 body example returns "tier": "Pro"; the admin tier-management operations use MarketMaker and Pro. The contract does not state a canonical casing.

  • 429 coverage is uneven. Several operations that are subject to the same rate-limit layer declare only 200 and 401 — both equity-history operations, both order-history operations, both cancel-on-disconnect GETs and PUTs, GET /withdrawals, GET /deposits, POST /deposits, GET /orders/{order_id}, and both rate-limit-status operations.

  • Retention windows unstated. Cursor pagination is bounded by "the retained window" and volume_30d may undercount when the fill buffer is at capacity, but no retention period or buffer size is given.

  • limit maximums are lower than the prose in two places. GET /withdrawals and GET /deposits both cap limit at 100 with a default of 100, so the parameter can only reduce the page — there is no way to page past 100 records (neither operation accepts a cursor).

  • EquityPoint.equity is a JSON number. Every other monetary field in this tag is a lossless decimal string. The contract itself notes the mismatch against PortfolioPoint.equity and tells clients to compare by decimal value, but the float representation remains on the wire.

  • early_access_allowed is present-or-absent, not true-or-false. The contract says it appears "only when the early-access gate is active" without stating what governs the gate or what its absence means for a caller.

  • Fee model is a draft. AccountFees.tier is always base, schedule is always standard, discounts is always empty, and FeeDiscount has no defined properties. Per-market effective rates are described as "a planned follow-up" with no date.

  • Position.leverage is permanently null today on every response that embeds a position (AccountSummary, AccountState), with leverage_error: "margin_state_not_mirrored". See GET /positions.

Admin

  • Tier names are not enumerated. No schema, no enum, no list. Only MarketMaker appears as an example here, and pro / marketmaker / unlimited appear prose-only in the RateLimitStatus description. An operator cannot learn the valid set from the contract.

  • Casing is inconsistent across the contract. These operations use MarketMaker and Pro; RateLimitStatus.tier is documented lowercase (pro, marketmaker, unlimited); the shared 429 body example returns "tier": "Pro". The contract does not state which casing is canonical or whether the comparison is case-insensitive.

  • No request or response schemas. All three operations carry examples only. Clients generated from the contract get untyped bodies.

  • 404 Address not in allowlist on delete, but no allowlist elsewhere. DELETE /admin/tiers/{address} returns 404 when the address is "not in allowlist", while PUT /admin/tiers documents no allowlist precondition and GET /admin/tiers describes its result as "overrides". The relationship between the override store and an allowlist is unstated.

  • No 401, no rate limiting declared. These operations declare only 200, 403, and (on delete) 404. There is no documented behaviour for a malformed Authorization header distinct from a wrong secret, and no rate-limit response — a bearer secret with no declared throttle.

  • No audit trail on the surface. Nothing in the contract exposes who set a tier or when. GET /admin/tiers returns the current state only.

Last updated