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/v1twins document a200with 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.PreviewResponsefield semantics. The eight fields are typed but carry no descriptions in the contract.Trigger direction. "Adverse" and "favorable" are not formally defined per
sidefor the stop and take-profit families.Time-in-force × conditional types. The contract does not state which
time_in_forcevalues are valid for the six conditional order types (for examplePostOnlyon aStopMarket).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_priceremoval. Marked deprecated with no stated removal version or date.Order.priceon market orders.Order.priceis typed as a non-nullable decimal string, whileOrderHistoryEntry.pricefor the same concept is explicitly nullable ("null for market orders"). WhatOrder.pricecarries for a market order is not stated.
Positions
leverageis permanentlynulltoday. The contract states it is "currently alwaysnull" withleverage_error: "margin_state_not_mirrored", and gives no target for populating it. Clients cannot read per-position leverage from this surface.No
market_idfilter on closed positions.GET /positions/closedaccepts onlylimitandcursor; there is no way to scope the query to a single market.Retention window unstated.
funding_paidis 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.429missing on closed positions.GET /positions/closedand its/api/v1twin declare only200and401; the429response the other position operation declares is absent from the contract even though the same rate-limit layer applies.Cross-margin only.
margin_usedis 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
directionis not enumerated.POST /account/margindeclares no request schema at all. Its example shows"direction": "add", and the summary and400description 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) andPOST /account/margin(request and response) carry examples only. Clients generated from the contract get untyped bodies for these.Two overlapping deposit paths.
POST /account/depositandPOST /depositsboth deposit collateral, return different shapes ({balance}vsDepositResponse), and the contract does not say which to prefer or how they differ operationally.Status vocabularies disagree.
Withdrawal.statusispending/settled/failed;FundsEntry.statusispending/confirmed/failed. The same lifecycle is named two ways.GET /orders/{order_id}has no/api/v1twin. 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.tieris documented lowercase (pro,marketmaker,unlimited); the429body example returns"tier": "Pro"; the admin tier-management operations useMarketMakerandPro. The contract does not state a canonical casing.429coverage is uneven. Several operations that are subject to the same rate-limit layer declare only200and401— 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_30dmay undercount when the fill buffer is at capacity, but no retention period or buffer size is given.limitmaximums are lower than the prose in two places.GET /withdrawalsandGET /depositsboth caplimitat 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 acursor).EquityPoint.equityis a JSON number. Every other monetary field in this tag is a lossless decimal string. The contract itself notes the mismatch againstPortfolioPoint.equityand tells clients to compare by decimal value, but the float representation remains on the wire.early_access_allowedis 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.tieris alwaysbase,scheduleis alwaysstandard,discountsis always empty, andFeeDiscounthas no defined properties. Per-market effective rates are described as "a planned follow-up" with no date.Position.leverageis permanentlynulltoday on every response that embeds a position (AccountSummary,AccountState), withleverage_error: "margin_state_not_mirrored". SeeGET /positions.
Admin
Tier names are not enumerated. No schema, no
enum, no list. OnlyMarketMakerappears as an example here, andpro/marketmaker/unlimitedappear prose-only in theRateLimitStatusdescription. An operator cannot learn the valid set from the contract.Casing is inconsistent across the contract. These operations use
MarketMakerandPro;RateLimitStatus.tieris documented lowercase (pro,marketmaker,unlimited); the shared429body 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 allowliston delete, but no allowlist elsewhere.DELETE /admin/tiers/{address}returns404when the address is "not in allowlist", whilePUT /admin/tiersdocuments no allowlist precondition andGET /admin/tiersdescribes its result as "overrides". The relationship between the override store and an allowlist is unstated.No
401, no rate limiting declared. These operations declare only200,403, and (on delete)404. There is no documented behaviour for a malformedAuthorizationheader 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/tiersreturns the current state only.
Last updated

