Skip to main content
Every change to the API is recorded here, newest first, with what it asks of your integration. Check it when you upgrade a client or when a response surprises you. The API is additive-only from the current baseline, except the groups marked PREVIEW. An entry marked breaking moved that baseline, which could only happen before the first partner integration. Versioning and stability says where the baseline stands today. Each entry keeps the words in use on the day it was written, so older entries may say “firm”, “partner” or “canonical book” where a newer page says venue or paper book. Core concepts has the words these docs use today.

2026-09-28: The margin preview gives a verdict on the paper book

POST /api/trading/margin-preview on a paper book account, a Demo included, now answers with the margin the order adds under the account’s risk policy and a real sufficient verdict. The paper book refuses an order that adds exposure when its margin, plus the commission of an order that fills now, doesn’t fit the account’s free collateral. The preview judges the order the same way at the moment you ask, so sufficient: false means that order would be refused now. Before this change the preview answered sufficient: null and described its figure as an estimate nothing enforced. estimate stays true, because the price and the free collateral can change before your order arrives. marginRequired and sufficient are null when there’s no verdict: there’s no price, a position can’t be valued, the risk policy doesn’t cover the instrument, or the order would be refused for another reason first. basis names the risk policy version the figure comes from, or says why there’s no verdict. A risk lock and your size limits aren’t judged by the preview. What it asks of your integration: if your interface ignored sufficient on the paper book, start reading it. Block or warn on false, as you already do for other providers. A stop-limit order’s preview reads its limit from limitPrice.

2026-09-28: Every risk lock says whether you can clear it

Every risk lock now carries clearable: in lock on GET /api/risk, PUT /api/risk and POST /api/risk/unlock, and in each lock event on /api/account/stream. It’s true when POST /api/risk/unlock would clear the lock now, and false while the settings are locked, for a failed evaluation, a firm halt or a drawdown rule, and for any lock that holds trading on an account bound to a risk policy. It’s true while nothing is locked. The field is additive. An integration that decided from lockReason whether to offer a way to clear a lock reads clearable instead, and offers it only while tradingLocked and clearable are both true. The unlock answers every call as it did before.

2026-09-28: Every venue response is the documented venue record (PREVIEW)

POST /api/operator/venues and POST /api/operator/venues/{venueId}/firm answered with the venue as it is stored, internal fields included (companyId, creationKey, creationHash and the stored switch), where the reference documents a VenueRecord. The venue read and the venue rules switch served the same internal fields beside the record’s own. Every route that answers a venue now answers exactly the VenueRecord: id, organizationId, legacyFirmId, name, description, environment, state, version, createdAt, rules and company. An integration that read companyId from one of these responses reads company.id.

2026-09-28: Level 2 is now venue rules (PREVIEW, breaking)

The venue’s switch for its own order limits, stages and analytics is now called venue rules, since “Level 2” reads to a trader as market depth. The meaning and the request body are unchanged; three names move, with no alias:
  • The switch is POST /api/partner/venues/{venueId}/rules and, for the back office’s signed-in session, POST /api/operator/venues/{venueId}/rules. The old /level2 path on either answers 404 not_found and changes nothing.
  • The venue read (GET /api/partner/venues/{venueId} and its operator twin) and the switch’s response carry the block as rules, the same shape the old level2 field had: { enabled, applies: { orders, fills, stages }, appliesAtProvider: { orders, fills, stages } }. No level2 field is served.
  • The operation’s summary is “Turn venue rules on or off”, so its reference page moved; the old page redirects to the new one. Its request and response schemas are VenueRulesRequest and VenueRulesResponse.
An integration changes the switch’s path from /level2 to /rules and reads venue.rules where it read venue.level2. Nothing else in a request or a response changes.

2026-09-28: Two venue operations are documented for a Venue key

GET /api/partner/venues/{venueId}/connections/{connectionId}/accounts lists the accounts a provider connection can reach, and POST /api/partner/venues/{venueId}/accounts/bind binds one of them to your venue. Both answer a Venue key, the first with the account:read scope and the second with connection:manage and account:issue. Until now the reference listed them only among the back office’s session routes. Nothing changes for an integration that already calls them.

2026-09-28: A futures order on a Demo opens the trader’s own market data

On the paper book, a request on the trader’s own signed-in session to POST /api/trading/order, /close, /reverse or /flatten opens the trader’s own market-data session for a futures instrument before the order is placed, as their chart does, and waits up to 5 seconds for it. A trader no longer needs a chart open for a first futures order on a Demo to fill. A request with a Trading API key opens none. When no session can serve the instrument, market_data_unavailable’s cause says why instead of feed_down, with three new values: login_missing (no market-data login the trader connected serves the instrument), feed_capacity (the engine could not open another market-data session; try again shortly) and feed_displaced (another application took over the market-data session on that login). An order that must fill now is refused with it and nothing written; a close and a reverse are refused with it before anything is claimed or cancelled; every other refusal of the order comes first as before; a limit or stop order still rests, and a flatten still runs. An order on an account whose risk decisions have not yet decided anything, such as a Demo opened a moment ago, waits up to 1 second for the first decision before answering 423 risk_locked.

2026-09-28: An order waiting on an instrument’s first price is refused as missing market data

On the canonical book, an account whose one hold is that the price lane of an instrument it holds has delivered no price yet (a position just opened on an instrument whose lane opens only once the account holds it) refuses an order on POST /api/trading/order that adds exposure on that instrument alone as 422 market_data_unavailable, with params { instrument, cause: "mark_missing" } and instrument the venue’s instrument id. It was 423 risk_locked with params.holds ["risk_uncovered"], which read as a risk setting. The order is still refused and nothing is written; send it again once the price arrives. An order adding to any other instrument on that account is still 423 risk_locked, and so is every addition while any other hold is on the account (a trading lock, a drawdown or stop-out latch, a liquidation incident), once coverage is lost after a price arrived (a gap, a stale or down source, an interruption), and every order on the replace, reverse and exits routes. An account whose risk decisions have not yet read any price or instant at all (its very first position, before any price arrived) can still answer 423 risk_locked there, since a risk term with an effective window or a day margin is not yet in force for it. GET /api/trading/actions still states the hold. Cancels, closes and reductions are unchanged.

2026-09-27: A close and its exits can name one position, and ticket accounts trade through every order route (PREVIEW)

On the canonical book, POST /api/trading/close and POST /api/trading/exits take an optional positionId, the position’s id as the account’s positions state it, and an optional positionRevision, the revision the trader saw. The close then fills only against that position, never another on the instrument and never past flat, and the exits protect that position alone. The book checks the position when the command commits: one no longer open on the instrument, held on the side the close trades, or smaller than the close is 422 position_not_closable, and one past the stated revision is 409 position_revision_stale, each with nothing written. A broker that names no positions answers 400 with code position_close_unsupported or position_exits_unsupported. On an independent-ticket account every entry opens a ticket of its own, whether it fills now or rests: market orders, marketable limits and brackets now fill there too. A bracket’s exits close its own ticket. A reduction that names no ticket, and exits set by instrument, are 422 position_mode_unsupported; name the ticket instead. POST /api/trading/flatten closes every ticket on the instrument.

2026-09-27: An issued account states whether it nets or holds independent tickets (PREVIEW)

Issuing an account on the venue platform takes an optional positionMode: net, one signed position per instrument and the default when the request states none, or independent_tickets, each opening order its own position, closed by naming it and never netted against an opposite one. Any other value is 400 invalid_request with field positionMode. A Personal Demo connect that opens an account takes the same optional positionMode, also netting by default. Independent tickets open only under a policy whose pool for the class states ticketHedging; otherwise the issue is 409 position_mode_unsupported and nothing is written. The issued account states its positionMode, and a retry with the same referenceId must state the same one. Publishing a risk policy version that states no ticketHedging for a class whose bound pools hold independent tickets is 409 risk_policy_ticket_hedging_required, and the version in force stays. A discovered provider account is bound in the model its provider reports, net or hedged; a hedged account cannot be a hedge destination (409 position_mode_unsupported).

2026-09-27: Every position has an identity, and every position read states the account’s position mode

positionModel on the account snapshot and every account frame is now net or independent_tickets, and GET /api/account/positions answers it beside the rows, read with them. The frame also carries the paper-book account’s economic cycle, which a reset advances, so a frame of an earlier cycle is recognizably about an account that no longer exists; it is null on a provider account. Every PositionRow on the paper book carries positionId, the ledger’s stable, opaque id for the position, and positionRevision, the position’s own revision: 1 when it opens and one more each time an execution changes it. A net position gets a new id each time it opens, so a reversal or a reopen after flat is a new position. On an independent_tickets account each open ticket is its own row under its own id, with its signed quantity, entry price (avgPrice) and its own unrealized profit, so a long and a short ticket on one instrument are two rows, never one net row. Each WorkingOrderRow names by positionId the ticket it opens or, when reduceOnly, closes or protects, so a ticket’s exits are the reduce-only orders naming it; on a net account a protective exit rested by a fill names the position it protects, and other orders name none. Both fields are null on every provider’s position and order, since trdrs never invents an identity for a provider’s position. GET /api/trading/position answers one net position per instrument, so on a ticket account it answers 422 position_mode_unsupported instead of a sum of the tickets. Firm account analytics states positionModel, and each open position and closed trade names its positionId; the venue risk desk names each position’s positionId and each account’s positionModel, and a ticket’s realized result is its own closed trades, never its instrument’s. A net account keeps its behavior; it gains the new fields.

2026-09-27: An immediate order is admitted again when the account moves under it

An immediate order on the canonical book reads the account and its prices, then admits itself. When another revision of the account commits in between (a funding or financing charge, a resting order’s fill, a balance operation), the engine now reads and admits the order again, up to three times, and answers 409 version_conflict only when the account kept moving each time. An order states no version of its own, so the conflict was never the client’s. A change to the venue’s configuration in between still refuses the order at once, since it was placed on terms that no longer hold. Nothing is written by an attempt that does not fill.

2026-09-27: An instrument of another class than the account’s pool holds is refused as its class

An order on the canonical book for an instrument whose product class is not the one the account’s pool holds (a CFD on a futures account, a future on an FX/CFD account, a perpetual on a futures account) is refused 400 asset_class_not_supported, with the instrument, its assetClass (cfd, futures or crypto) and provider paper, the code the route answers for a class a provider does not trade. It was collateral_policy_unverified with cause contradictory, which read as a policy fault. An account’s instrument catalog states the same refusal as the instrument’s poolRefusal. Nothing is written either way.

2026-09-26: An imported paper account is served from the canonical book

Once the cutover imports a paper account from the Demo book, every trader route (orders, fills, positions, balances, the instrument list, competition entry) reads and writes it on the canonical book under the same account number. Its paper connection states the instant it opened on the canonical book, its import, where it stated the Demo account’s creation. A trader’s instrument list comes from the account’s venue catalog on every deployment. Its fills in the same second keep the order the ledger recorded them in on the executions route, the later one first. No field changes shape.

2026-09-26: Orders on trdrs CFDs (PREVIEW)

POST /api/trading/order and the other order routes accept a trdrs CFD named by its contract id (TRDRS:EURUSD, for example) on an account on the canonical book whose pool holds FX and CFDs, sized in lots on the contract’s grid (qty at least 0.01, in steps of 0.01). A CFD fills at the account’s executable quote: the source’s bid and ask rounded outward to the tick, then widened by the pool policy’s markup. Nothing fills outside the contract’s trade session (contract_not_trading, phase session_closed), and a resting order waits for the session. On any other account, and on every other provider, the order is refused 400 asset_class_not_supported with assetClass cfd, before anything is written. The source’s own symbol (EURUSD) names no contract and is unknown_instrument. A commission in basis points on a CFD quoted in another currency than the account’s (TRDRS:USDJPY in a dollar account) is converted into the account’s currency at the fill, at the side worse for the account; with no conversion quote in force the order is refused collateral_unvalued with cause conversion_unavailable or conversion_stale, and nothing is written.

2026-09-26: collateral_unvalued names a conversion it could not price

The collateral_unvalued refusal’s cause has two new values: conversion_unavailable and conversion_stale. Each says the requirement of the order’s instrument is stated in another asset than the account’s currency (a currency pair’s margin, in its base currency) and the conversion that carries it into the account’s currency has no usable price, or only one older than its route allows. instrument names the instrument whose requirement needed it. Nothing was written. A requirement is converted at the side that makes it larger, so a wider spread never lowers it.

2026-09-26: Personal Demo version 2026-09-26.3 states its fill terms: no commission and no markup

Personal Demo policy version 2026-09-26.3 succeeds the unpublished 2026-09-26.2 and, like it, is in force only once trdrs publishes it. Its only change is a fill term in both its futures and its crypto derivative pool, demo-execution-2026-09-26.3: no commission, no markup, and a collar of no ticks, each a trdrs term with its reason, the commission counting contracts in the futures pool and base units in the crypto derivative pool. A policy’s pool may state such a term (execution: the commission, the markup as bidTicks and askTicks as a CFD pool’s markup rows state it, and the collar, each figure sourced) only when the policy is trdrs’s own; a firm’s accounts fill on its venue’s conditions, and a firm policy stating execution is refused.

2026-09-26: Analytics and stage eligibility read the stage the cycle is judged by (breaking)

An account’s analytics and stage eligibility no longer take their session or stage rules from the request. They measure against the stage version the account’s running cycle is judged by, as its venue published it.
  • GET /api/partner/accounts/analytics and GET /api/partner/accounts/eligibility no longer take timeZone, rolloverHour, stageId, profitTarget or minimumTradingDays; a request that sends them has them ignored. Trading days are counted in the stage’s session, or in the 17:00 America/Chicago roll for a cycle no stage judges, and tradingDays.session states which.
  • The eligibility check states the stage it measured in stage and answers 409 stage_unstated for an account whose cycle no stage judges. The legacy door issues into a group running no stage, so its accounts answer that; the venue’s stage eligibility route is the check for staged accounts.
  • The venue analytics route (…/venues/{venueId}/accounts/{accountId}/analytics) and the venue risk book (…/venues/{venueId}/risk) no longer take timeZone or rolloverHour; each account’s days are counted in its own cycle’s stage session.

2026-09-26: A commission states its timing, direction and unit (PREVIEW, breaking)

Every commission a condition profile, group override or preview states now requires timing (fill, charged as each fill lands, or round_turn, nothing on the entry and the stated round-turn amount on the quantity each reducing fill closes) and direction (entry, exit or both; a round_turn commission is stated with both, else 400 round_turn_is_both), and a perUnit commission requires unit: "quantity": the amount counts each unit of the instrument’s own quantity unit, since the conditions cover every instrument. A venue that charges its product classes different per-unit rates states each through a group of its own. A write that omits a field is refused 400 with field naming it. Every stored profile and group states timing: "fill", direction: "both" and, per unit, unit: "quantity", which is what it always charged, so no account’s charges change. venue_fill_refused gains the reason commission_unit_mismatch, for a per-unit commission a term scopes to contracts, lots or base units on an instrument counted in another unit.

2026-09-26: A CFD’s financing comes from the venue’s published risk policy (PREVIEW, breaking)

A condition profile, group override or preview that states financing is refused with 400 financing_superseded, field naming it, as margin is: a venue’s financing is the financing term of its published risk policy version, which states each CFD’s long and short annual rates and the rollover rule it is financed on, either value dates on a business-day calendar or a multiplier for each weekday (three on Wednesday, for example). The conditions’ financing field is removed from VenueConditionProfile and VenueGroupOverride, and removed from every stored profile and group; no financing was ever charged under it. A CFD position on the canonical book is financed once at each rollover of its row’s rule, at the executable side worse for the account (the ask when the rate charges, the bid when it credits), in its profit asset, converted into the pool currency along the pool’s declared route; a charge whose price or conversion quote is not journaled at the rollover is owed with an unknown amount, never zero, and holds the account’s additions until it is priced. The analytics fields that count financing are unchanged.

2026-09-26: Canonical analytics and the venue risk book state money exactly, half to even

An account on the canonical book states its analytics money (realized profit, fees, net trading, balance, unrealized profit, equity and each closed trade) computed exactly and rounded once, at the precision the canonical book’s posting term states for the account currency, half to even. A value on a tie, such as an equity of 50,070.625, is stated as 50,070.62 where it could before be stated as 50,070.63. The venue risk book sums money the same way per currency, so a USDC book’s totals, notional, exposure and history carry all six decimals where they were stated in cents, and exposure money across more than one currency is null. No field changes shape.

2026-09-26: An issued account’s pool may hold FX and CFDs (PREVIEW)

POST /api/partner/venues/{venueId}/accounts accepts riskPolicy.profile fx_cfd, beside futures and crypto_derivative: the account’s pool is bound to the venue’s published policy for the FX/CFD class, which that policy must offer (risk_policy_profile_not_offered otherwise, as for any class). An account holds one class, so its pool is still named by its currency. The issued account answers the class in riskPolicy.profile. Orders on an FX or CFD instrument are still refused until the canonical book trades the class.

2026-09-26: A challenge enrolls through a venue’s staged group, under the stage’s rules (preview)

POST /api/challenges/enroll issues the evaluation account on the canonical book into the venue group the challenge maps the size to, under the stage that group runs, and the enrollment is judged by that stage’s rules.
  • GET /api/challenges answers a new field, sizes: each size a program enrolls at, with the venue group it is issued into and the stage terms it runs (target, minimum trading days, session, drawdown, return cap and duration, in money for that size). A program with sizes states its rules only there, and its percentage rules (profitTargetPct, maxTotalDdPct, ddMode, minTradingDays, allowWeekendHold) are null. An enrollment’s ddMode is null when its group’s stage judges it.
  • The enroll route answers 409 with a code, with nothing written, for a program that maps no size yet (venue_required), a size it does not map (challenge_size_unmapped), a group that runs no stage (challenge_group_unstaged) or has no route (group_has_no_route), a venue with no single policy in the program’s currency (venue_risk_policy_unresolved), and an active enrollment (enrollment_active); a currency the deployment does not issue is 400 currency_not_issuable.
  • An enrollment’s progress on the canonical book is measured by its stage: the target and a static drawdown’s floor as levels over the starting balance, and trading days in the stage’s session. A trailing floor, a daily anchor and a high-water mark are null, as they are the risk decisions’ own state.

2026-09-26: A funding charge that can never be priced is resolved by a trdrs operator (PREVIEW)

A perpetual’s funding charge owed on an account on the canonical book, whose provider never records the rate or which has no mark on either side of its boundary within the row’s bound, is resolved by a trdrs operator, never automatically: either posted once at the rate the operator states with its source, on the charge’s recorded basis or, where none was recorded, on a basis the operator states, or ended with no charge and no journal row. Either way the account takes new exposure again once it owes nothing. Nothing else changes: a charge whose rate is recorded late still posts as it lands.

2026-09-26: Personal Demo version 2026-09-26.2 takes a perpetual’s funding basis from the first mark after the hour

Personal Demo policy version 2026-09-26.2 succeeds the unpublished 2026-09-26.1 and, like it, is in force only once trdrs publishes it. Its only change is each Hyperliquid perpetual’s funding row, term demo-crypto-funding-2026-09-26.2: with no Hyperliquid mark in force at the hour, a charge takes the first mark received within 10 seconds after it (basis.after: "first_within_bound"), a trdrs choice whose reason sources.basisAfter states. A mark in force before the hour always wins. A charge with no mark on either side within the bound stays owed until a trdrs operator resolves it.

2026-09-26: A funding row may take its basis from the first mark after a boundary (PREVIEW)

A crypto derivative policy’s funding row may state basis.after: "first_within_bound", sourced as sources.basisAfter: where no mark is in force at a boundary, the charge takes the first mark received after it within basis.maxAgeMs. A mark in force before the boundary always wins, and a row without after, or with "none", takes no later mark. Each charge records its basis once, the first time it is determinable, and never revises it; a boundary with no mark on either side within the bound stays owed until its funding is resolved. A version that states after without sources.basisAfter, or sources.basisAfter without after, is refused as it is today for any unsourced or unknown field.

2026-09-26: Analytics read an account that closed a position, and say why risk coverage is incomplete

GET /api/partner/accounts/analytics, GET /api/partner/accounts/eligibility, GET /api/partner/venues/{venueId}/accounts/{accountId}/analytics and GET /api/partner/venues/{venueId}/risk answer for an account on the canonical book that closed a position in its running cycle. Before, each answered 503 venue_service_unavailable (or failed on the legacy routes) for such an account.
  • A closed trade’s entryPrice is the price its position was entered at, recovered exactly from the close’s realized profit and loss; its quantity is the size closed, never negative.
  • A new field, completeness.riskReasons, names why completeness.risk is false: the risk decisions’ own coverage reasons, such as input_pending or undecided, or risk_coverage_started_after_cycle for an account on the paper book. It is empty when completeness.risk is true.
  • A venue route that cannot answer names the book’s reason where it stated one (risk_input_busy, risk_input_halted, risk_membership_unknown, book_snapshot_mismatch, venue_revision_manifest_missing, risk_checkpoint_mismatch), still as 503.

2026-09-26: The legacy issue call issues through your venue, onto the canonical book

Breaking. POST /api/partner/accounts issues only through the venue that adopted the firm, and this entry moves the compatibility baseline.
  • Each item opens its account on the canonical book, as the venue twin POST /api/partner/venues/{venueId}/accounts does: the account joins the venue’s one routed group with no stage, and its pool binds to the venue’s one published risk policy in the item’s currency offering the class that currency trades, futures for USD and crypto derivatives for USDC. The request is unchanged: it names no group and no policy.
  • Three new per-item errors, each with nothing written: venue_required for a firm no venue has adopted, venue_group_ambiguous for a venue with no routed group without a stage, or several, naming them in a new groups field, and venue_risk_policy_unresolved for a venue with no such policy, or several. A firm creates a venue as the owner of the firm its Partner key belongs to, and the venue adopts that firm.
  • The account trades what its venue lists and activates, of the class its pool’s policy states terms for; its money is posted at its currency’s precision, six decimals for USDC. The balance ledger’s provision row is gone: the starting balance is the account’s one opening allocation on its ledger. A reference used before this change still answers its account, from the book that holds it.

2026-09-26: The risk state shows every authority’s limits on an account on the canonical book

GET /api/risk and PUT /api/risk answer a new field, limits, on an account on the canonical book: the version of its limits it is decided under now (inForce) and a version recorded since, which comes into force at the next session day (next), each term with the authority that states it (trader, firm or venue), the end-of-day close with its authority, and the session the day and week are counted in. It is read-only: settings stay the trader’s own controls, the only terms the trader writes, so a firm’s limit and a venue’s limit show beside them and are never theirs to change. It is null on an account the controls alone bind. Apps should show the firm’s and the venue’s terms read-only beside the trader’s own.

2026-09-26: An account’s instrument list states what its pool refuses (preview)

GET /api/partner/venues/{venueId}/accounts/{accountId}/instruments states, per instrument, what the account’s pool itself refuses an entry for under the risk policy it is bound to, in the new additive field poolRefusal: the catalogued code and parameters an entry order would be refused with, or null where the pool can hold the instrument. A perpetual that settles in USDC on a USD account your venue issued now reads currency_economics_unsupported here, where before it was listed with nothing in the way and refused only when ordered; an account whose pool is not bound yet reads collateral_policy_unverified for each instrument. Only what holds whatever the hour is stated: a contract past its roll or a day window closing to new exposure is not listed. basis is now account_configuration_and_policy. The trader’s own instrument list offers only what the pool can hold.

2026-09-26: An account moved onto the canonical book keeps its daily and weekly limits

An account moved from the paper book onto the canonical book keeps the loss, profit and end-of-day limits set for it on the paper book, where they stood at the move: the day and the week count from the same starting points, and a limit it had already reached still holds it until that window ends. The limits become your firm’s terms on the account, so the trader cannot loosen them; the trader’s choice to lock their settings while a limit holds carries over as theirs. A halt your firm placed stays until your firm resumes the account. The venue’s own daily and weekly loss limits, which the paper book never enforced, apply to a moved account from the next session day on.

2026-09-26: Analytics read an account on the canonical book from its ledger and risk decisions

GET /api/partner/accounts/analytics, GET /api/partner/accounts/eligibility and GET /api/partner/venues/{venueId}/accounts/{accountId}/analytics measure an account on the canonical book from its ledger and its risk decisions. Before, they read the paper book’s rows, which an account the venue issued onto the canonical book does not have, and which an account moved there stopped updating.
  • Two additive fields: pnl.imported, the result a cycle moved from the paper book carried over, and tradingDays.imported (count, lastTraded and the session they were counted in), the days it had counted. Both are null for a cycle no import opened, and for every account on the paper book. netTrading includes pnl.imported.
  • grossRealized is realized P&L and corrections; fees are commission, fees, financing and funding; money is stated at the account currency’s own precision. A fill is dated by the instant the ledger recorded it.
  • unrealized and equity are the risk decisions’ own valuation, null while they have not valued the account’s current book or their inputs do not cover it; completeness.risk states that coverage. Breaches are the decisions’ latches of the cycle and its liquidations.
  • The eligibility check’s tradingDays is null, with the reason imported_days_other_session, for a moved cycle checked in a session other than the one its imported days were counted in.
  • equityHistory samples an account on the canonical book from its ledger and its risk decisions’ valuation, under its canonical cycle, with a null equity while they have not valued it. GET /api/partner/venues/{venueId}/risk measures it the same way, pricing its positions at the decisions’ own valuation.

2026-09-26: An order the market cannot fill now says why

On the trading routes, an order that must fill now on an account on the canonical book, refused because of its prices, answers the code that says why, never invalid_request:
  • market_data_unavailable now carries cause, on every account: feed_down, feed_unreconciled, feed_other_contract, quote_missing, quote_one_sided, quote_invalid, mark_missing, source_mismatch or price_time_invalid. A price that is only old stays market_data_stale, whose ageSeconds is never negative; a price stamped ahead of the engine’s clock is price_time_invalid.
  • venue_fill_refused is new, with instrument and reason: outside_collar when the venue’s markup takes the executable price past its route’s collar, beyond_limit when it takes it past the order’s limit. An arriving limit order refused this way is refused whatever its time in force, and never rests, as the paper book rejects the same order.
  • A stop-limit whose trigger is spent, on an account a venue issued onto the canonical book, takes the market on the venue’s taking terms (the markup inside the collar, never past its limit) on a quote that crosses its limit, as on the event that spent its trigger; a print through its limit still fills it at its level. Before, a crossing quote after the trigger filled it at its level with no markup.
  • An order the venue’s own session closes, reached while it waited for a price, answers contract_not_trading with phase session_closed, as the provider’s own session check does, where it answered 403 permission_denied.
  • A quote the book was never handed answers 422 market_data_unavailable (quote_missing), where it answered 404 not_found.
  • invalid_request now carries field and reason, the path and the code of the value refused, both null where the request’s shape as a whole is refused. An order outside the instrument’s own terms that must fill now (an order type the instrument does not list, a quantity out of its range or off its grid, a price outside its bands or off its grid) answers 400 invalid_request with them, where it failed with a server error.
A Hyperliquid perpetual in a flat market no longer reads stale: the venue restates its mark on every block, and the engine takes that restatement as a fresh observation at most every 500 ms, so a position valued on an unchanged mark stays covered and fills on it while the venue is live.

2026-09-26: A reset of an account on the canonical book runs in its own ledger

POST /api/partner/accounts/reset and POST /api/partner/venues/{venueId}/accounts/{accountId}/reset reset an account on the canonical book in its own ledger, on the account’s lane: to the balance its running cycle started at, with the reset recording your firm, the actor and the reason, and account.reset queued in the same commit. Before, a reset of an account the venue issued onto the canonical book answered 404, and one moved there from the paper book was refused. The refusals keep their codes: not_settled also covers a command on the account’s lane whose outcome is not yet known. New answers, for an account on the canonical book: 423 reconciliation_hold while it is held for reconciliation, and on the partner route 503 account_unavailable (with Retry-After) when its lane cannot take the reset now, and 503 outcome_unknown when the reset’s outcome was not learned. A reset’s referenceId is your firm’s once across every account, on either book, as a balance operation’s is: one your firm already used to reset any account answers duplicate and moves nothing.

2026-09-26: A stage advance issues a paper account’s successor on the canonical book (PREVIEW)

POST /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/advance issues a paper account’s successor on the canonical book, as every account a venue issues is: one allocation of startingAllocation, in the group the policy names, with its first cycle under the stage that group runs and its pool bound to the risk policy its source’s pool is bound to. A source with no bound pool takes the venue’s one published futures policy in its currency; with none or several, the advance answers 409 not_eligible with successor_policy_unresolved. Before, the successor of an account on the paper book was opened on the paper book, and an advance from an account on the canonical book could not issue one. The successor carries the issuing firm’s provenance under the transition’s reference, as before.

2026-09-26: Stage decisions on the canonical book count the risk decisions’ trading days (PREVIEW)

GET /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/eligibility measures an account on the canonical book from its ledger and its risk decisions, including an account the venue issued there on the paper book, which was measured from the paper book’s rows it does not have.
  • facts.tradingDays is the count the account’s risk decisions keep: the session days, in the stage’s session, on which the cycle’s positions changed, starting from the days the paper book counted for a cycle that moved to the canonical book. It is null until the decisions take the cycle under its stage. Before, it counted the ledger’s fills by when each traded.
  • facts.accountRevision and facts.decidedRevision are new: the head revision the facts were measured at, and the account revision the risk decisions had decided through. Both are null for an account with no canonical book.
  • facts.netTradingPnl includes funding charges and, for a cycle that moved to the canonical book, what it traded on the paper book before it moved.
  • facts.breaches names every latch the decisions made in the cycle by its rule (max_drawdown, daily_drawdown, daily_loss, weekly_loss, daily_profit, weekly_profit, eod_close, maximum_return, duration_expired) beside stop_out, and facts.observedRules names the stage’s own drawdown, cap and duration rules once the decisions take the cycle under it, and the rules of the limits in force at their last decision.
POST /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/advance on an account on the canonical book answers 409 stage_decision_pending while its risk decisions have not decided through its head. It clears without any action: retry it. An account whose pool is bound to no risk policy has no decisions to wait for, so its trading days stay unknown and the advance answers not_eligible with incomplete_history. Facts measured at a head that has since moved answer 409 version_conflict.

2026-09-26: Account lists read an account on the canonical book from its ledger

GET /api/partner/accounts and GET /api/partner/venues/{venueId}/accounts state an account on the canonical book from its ledger: its balance, the balance its running cycle started at, and when it was last reset. Before, the partner list read the paper book’s row, which an account the venue issued onto the canonical book does not have (it answered a zero balance), and the venue list stated no starting balance for it. An account that moved onto the canonical book is no longer stated as it stood when it moved. Each row of GET /api/account/orders documents filledQty, which it has always carried: how much of the order filled, or null where the venue reports no such number.

2026-09-26: A crypto perpetual on a canonical USDC account pays and receives funding (PREVIEW)

A crypto perpetual on an account the canonical book holds now takes exposure where the account’s risk policy states a funding row for it. At each boundary of the row’s schedule, each position pays or receives its funding once: its quantity, times the contract’s value per price unit, times the mark at the boundary, times the provider’s settled funding rate for that boundary, a long paying a positive rate and a short receiving it, posted in USDC’s six decimals. Until the provider’s settled rate is recorded, within the row’s publication grace the boundary waits, and past it the charge is owed: an order that adds exposure is refused collateral_unvalued with params.cause obligation_pending until the rate is recorded and the charge posted. An owed charge is never posted as zero or at an estimate. An order on a perpetual whose policy states no funding row for it is refused collateral_policy_unverified with params.cause missing. Before, every order on a perpetual that added exposure was refused product_economics_unsupported.

2026-09-26: An account on the canonical book sends its risk and balance events

A firm’s account on the canonical book now sends risk.locked and risk.unlocked for every lock and unlock, and balance.recorded for every balance operation it applies, as an account on the paper book always has. The payloads are unchanged; balance.recorded states the amount the operation posted. Before, an account the venue issued onto the canonical book sent neither lock event and no balance event.

2026-09-26: Brackets and stop-limit orders on a canonical account

An account the canonical book holds now declares nativeBracket and stopLimit in its capabilities, and no bracketEntryTypes, so POST /api/trading/order takes a stop-loss and take-profit bracket on any entry type and a stop_limit order there, where both answered 400. The request shapes are unchanged.
  • A bracket’s exits rest the moment its entry fills, in the same commit: a reduce-only stop and a reduce-only target that cancel each other, each sized to what that fill opened. An entry that rests refuses an exit on the wrong side of its price, a limit’s level, a stop’s trigger or a stop-limit’s limit; an entry that fills now rests its exits as stated. A tick distance is measured from the price the entry fills at, or for an entry that rests, from its price. The account’s protection lane shows the bracket from the moment the order is accepted.
  • When a fill closes a position, flat or past flat, every other reduce-only order on that instrument ends with endReason position_closed, so an exit placed for one position never fills against the next.
  • A stop-limit rests with its trigger and its limit. Once a price crosses its trigger it rests as a limit, and its order row reads orderType: limit with no trigger; replacing it as a stop-limit then answers 409. On the price that crosses its trigger it fills only where the market is already through its limit, at the venue’s price for an order that arrives then, never past its limit.
  • An order that could add exposure and whose venue commission is in another currency than the account’s is now refused as it is placed, currency_economics_unsupported (422), where a resting one was accepted and never filled.

2026-09-26: An order row states why the order ended

Each row of GET /api/account/orders gains endReason: trader_cancelled, oco_sibling_filled, replaced, liquidation_cancelled, position_closed, import_entry_cancelled or import_replaced. It is null on an order still working, filled or rejected, and on every order a venue ends without stating why, which is every external broker’s today. An account that moved onto the canonical book lists the working orders the move ended beside its own, each cancelled with import_entry_cancelled (an order that could add exposure) or import_replaced (a protective order the same order on the canonical book replaced). The list of reasons grows: show an unknown value as it is.

2026-09-26: A balance operation’s reference reads back from either book, and stays the firm’s once

GET /api/partner/balance-op?referenceId=, and its venue twin, now answer an operation on an account the canonical book holds, where they answered 404: the account, the operation as the firm sent it (credit, debit or adjustment), the amount at the currency’s posting precision, applied and the instant it applied. A reference the firm used on an account before that account moved onto the canonical book answers as it did before the move, legacy_unknown included. The shape is unchanged. A firm’s referenceId is the firm’s once across all its accounts, as it always has been, now on the canonical book too: reused on any other account, or twice at once on two accounts, it applies once and every other attempt answers 409 with duplicate: true.

2026-09-26: A canonical book write’s refusal names its own code

An order the canonical book refuses in its own transaction now answers a code of its own, each with no parameters, where it answered order_rejected with only a sentence to tell them apart: not_found (404), permission_denied (403), invalid_request (400), version_conflict (409), idempotency_conflict (409), position_mode_unsupported, product_economics_unsupported and currency_economics_unsupported (422). The status and the error sentence are unchanged. A refusal the catalogue does not name stays order_rejected, its sentence naming the reason. An order on a perpetual in a canonical account, which the book does not value yet, answers product_economics_unsupported.

2026-09-26: A canonical account’s orders meet the venue’s terms on the canonical book alone

An order on an account the canonical book holds is admitted by the canonical book alone. The paper book’s venue admission, which looked a listing up under the instrument’s root and valued a market order on the paper tape, no longer reads it: an order on a dated contract the venue lists (CME:NQZ2026) is no longer refused instrument_not_permitted, and a market order is no longer refused valuation_required where the paper tape had no price. Each gets the canonical book’s own answer, such as contract_not_trading outside the contract’s session. On such an account, an order that adds exposure, whether it fills now or rests, meets the venue’s terms in its own transaction and is refused with the venue admission codes: venue_conditions_missing, venue_route_missing, venue_route_retired, venue_instrument_disabled, venue_instrument_expired, entry_halted, instrument_not_permitted, below_min_notional and reduce_only_violation, with params.instrument the venue’s instrument id. An order that fills now answered these as order_rejected, or as a 500, before. A reduction passes every one of them. It fills at the executable side of a fresh quote, on the terms the venue states, or with no markup, collar or commission where the venue states no conditions or no route; an order that fills now was refused there before. The account’s action verdicts state the same refusals for open, and none of them for protect, reduce or flatten.

2026-09-26: A risk-terms refusal can name a perpetual’s funding term

The term parameter of risk_terms_missing, risk_terms_stale and risk_terms_contradictory gains funding: a perpetual’s funding row, which states the venue whose settled funding rate it is charged, its boundaries, its sign and the price its notional is taken at. A perpetual takes new exposure only while its account’s risk policy states a funding row for it, in force and charged from the venue that prices it; a position already held is still valued without one. Nothing raises it yet: the canonical book still refuses perpetuals on every order path. A client that translates term keeps a generic wording for a value it does not know, as the catalogue already requires.

2026-09-26: A venue’s USDC account posts its money in six decimals (PREVIEW)

POST /api/partner/venues/{venueId}/accounts, and its operator twin, now open a USDC account on the canonical book where the deployment issues USDC (a sandbox): its pool is bound to a venue policy in USDC that offers crypto_derivative, and its money is posted, held and stated in USDC’s six decimals, so the answer’s balance has six places ("50000.000000"). A USD account keeps cents. A balance operation on a canonical USDC account takes up to six decimals, and a finer amount is 400 amount_precision_exceeded with params.decimals 6; a USDC account the canonical book does not hold keeps two. A crypto perpetual pays funding the canonical book does not record yet, so no instrument adds exposure in a USDC pool yet: its orders that add exposure are refused, as before. POST /api/partner/venues/{venueId}/risk-policies, and its operator twin, refuse a version whose posting term states a currency at another precision than the canonical book posts it at (USD in two decimals, USDC and USDT in six) as 409 risk_policy_posting_mismatch, storing nothing.

2026-09-26: A balance operation on an account held for reconciliation answers 423

POST /api/partner/balance-op and the venue balance route, POST /api/partner/venues/{venueId}/accounts/{accountId}/balance and its operator twin, answer a credit, debit or adjustment on an account the canonical book holds, while that account is held for reconciliation, with 423 and the code reconciliation_hold, recording nothing. The legacy route answers { "error": ..., "code": "reconciliation_hold", "params": {}, "referenceId": ... } and the venue route { "error": "reconciliation_hold" }. The same reference can be sent again once a trdrs operator releases the hold.

2026-09-26: The Partner API refuses a withdrawal it cannot cover

POST /api/partner/balance-op, on an account the canonical book holds, answers a debit it cannot make with a new 409: { "code": "balance_refused", "reason": ... }, where reason is insufficient_funds (the pool’s free collateral is less than the amount), permission_denied (a hold stands on the account) or invalid_request (a held position cannot be valued now). Nothing is recorded, and the same referenceId may be sent again once the cause clears.

2026-09-26: A venue issues accounts on the canonical book, in USD or USDC, bound to its risk policy (PREVIEW)

POST /api/partner/venues/{venueId}/accounts, and its operator twin, in the venue account routes, a PREVIEW group, now open the account on the canonical book. The body adds two required fields: currency, the pool the account is held in (USD everywhere, USDC in a sandbox; any other is 400 currency_not_issuable with params.currency and params.environment), and riskPolicy, { policyId, profile }, one of the venue’s published risk policies and the product class the pool holds, futures or crypto_derivative. The account opens with its starting balance, in its group under the stage version the group runs, with its pool bound to that policy’s version in force and the venue’s daily and weekly loss limits in force recorded on it. A policy that is not published, is in another currency, does not offer the class or belongs to another venue is refused with 409 risk_policy_unpublished, risk_policy_currency_mismatch, risk_policy_profile_not_offered or risk_policy_authority_mismatch, before anything is written. The answer adds book: "canonical" and riskPolicy with its version and sequence; balance is the ledger’s. The same referenceId with the same body answers the same account with created: false and 200; with any other body it is 409 idempotency_conflict. A balance operation on such an account moves its ledger: a credit or an adjustment posts, and a debit is a withdrawal of free collateral, refused with 409 balance_refused and a reason when the pool cannot spare it or a hold stands.

2026-09-26: An account held for reconciliation refuses new exposure

trdrs recomputes every committed change to an account on the paper book independently. When the book does not follow from what happened to it, or one effect was counted twice, the account is held for reconciliation until a trdrs operator investigates and releases it. While it is held, the order, reverse and replace routes answer an order that may add exposure with 423 and the new code reconciliation_hold, which carries no parameters; a reduce-only order, a close, a flatten and a cancel stay available, and the account takes no credit, debit or reset. The book is never rewritten by the hold. GET /api/trading/actions states the same refusal in its open verdict.

2026-09-26: A venue stores and publishes its own risk policy (PREVIEW)

/api/partner/venues/{venueId}/risk-policies, /api/partner/venues/{venueId}/risk-policies/{policyId} and /api/partner/venues/{venueId}/risk-policies/{policyId}/publications, and the same routes under /api/operator/venues/{venueId}/risk-policies, /api/operator/venues/{venueId}/risk-policies/{policyId} and /api/operator/venues/{venueId}/risk-policies/{policyId}/publications, a PREVIEW group, store, publish, list and read the account risk policies a venue states for its own accounts: the margin, stop-out, recovery, valuation, settlement and posting terms its issued accounts are held to. POST .../risk-policies with { policy } stores one immutable version (201, or 200 with created: false for the same content again); GET .../risk-policies/{policyId} reads one policy with each version’s content; POST .../risk-policies/{policyId}/publications with { version } puts it in force, recording it on every account pool bound to the policy in the same transaction, with the Idempotency-Key as the publication’s reference. Both need venue:configure; the reads need venue:read. A policy has one owner: a version under another venue’s or trdrs’s policy id is 409 risk_policy_owner_conflict, a policy id beginning trdrs- is 409 risk_policy_id_reserved, other content under a stored version is 409 risk_policy_version_conflict, a version with an unsourced figure is 409 risk_policy_unsourced, and the version already in force is 409 risk_policy_unchanged.

2026-09-26: An entitled instrument names a source trdrs serves per trader (PREVIEW)

An instrument candidate that names an entitlement, in the venue instrument routes, a PREVIEW group, is priced from each trader’s own feed, so its pricing.sourceId must be a source trdrs serves per trader, rithmic today. Any other is refused with 400 entitled_source_unserved, field instrument.pricing.sourceId, when the candidate is saved, and again when a candidate saved earlier is activated. Before, such an instrument activated and its orders that add exposure were refused one by one; no account could ever be valued on it.

2026-09-26: The server time carries milliseconds

GET /api/market/time answers timeMs, the engine clock in epoch milliseconds, beside time, which is unchanged. Both come from one reading. A client that times the request knows the engine read timeMs within the round trip, so it can bound its clock offset from the engine to half the round trip, where the whole second in time leaves up to a second.

2026-09-26: A risk-terms refusal can name the terms of an FX or CFD pool

The term parameter of risk_terms_missing, risk_terms_stale and risk_terms_contradictory gains four values for the terms an FX or CFD instrument is bound by: cfd_margin (its margin row), markup (how far its source quote is widened into the executable quote), financing (its rates and the rollover rule they are charged on) and conversion (the route a currency other than the account’s is converted along). Nothing raises them yet: no account’s pool is bound to FX or CFD terms, and FX and CFD instruments stay refused on every order path. A client that translates term keeps a generic wording for a value it does not know, as the catalogue already requires.

2026-09-26: Venue loss limits are enforced where the venue’s accounts are decided (PREVIEW)

dailyLossLimit and weeklyLossLimit in VenueRiskPolicy, in the venue conditions routes, a PREVIEW group, are now taken while every account the venue issued is bound to its published risk policy version. The write that saves a limit records it on every account it applies to, which counts it from its next session day; a breach holds the account, and an order that adds exposure is refused, until that session day ends. Where some account is not bound, such as one held at a provider, a limit is still refused with 400 loss_limit_unenforced, and the effective conditions report each limit’s enforced as whether it is.

2026-09-26: A stage states a consistency cap and a duration (PREVIEW)

VenueStagePolicy, in the venue stage routes, a PREVIEW group, gains two optional terms, each null when a policy does not state it, which is how every policy stored before reads. maximumReturn is a return on equity, in money above the cycle’s starting balance, at or above which the cycle fails. maximumDays is the whole days a cycle may run from its first trade before it fails. A breach of either flattens the account and locks it with reason eval_breach until the account’s reset. Both are economic, as the drawdown rule is: activating a policy that adds or moves one waits for the accounts to be flat.

2026-09-25: A venue’s margin comes from its risk policy, not its conditions (PREVIEW)

margin in a venue condition profile and group override, in the venue conditions routes, a PREVIEW group, was saved and applied to no trading: what an account must hold comes from the venue’s published risk policy version, the one authority. A profile, group override or preview that states a margin policy is now refused with 400 margin_policy_superseded, field naming it; the field takes null. A profile or override stored before the refusal still reads with its value, and the effective conditions report it as applied: false.

2026-09-25: Venue loss limits are refused until they are enforced (PREVIEW)

dailyLossLimit and weeklyLossLimit in VenueRiskPolicy, in the venue conditions routes, a PREVIEW group, were saved and never applied to trading. A limit that is stored and not enforced reads as a protection the account does not have, so a profile, group override, preview or account override that states one is now refused with 400 loss_limit_unenforced, field naming the limit. Both fields stay in the shape and take null. A profile or override stored before the refusal still reads with its value, and the effective conditions report each of the two as enforced: false.

2026-09-25: The margin_stop_out lock reason

  • lockReason on the risk lock, and reason on a risk.locked webhook, can be margin_stop_out: a margin stop-out the account’s risk decisions recorded on the event-driven risk path of the paper book. It holds until the account is shown recovered on current prices; the manual unlock does not lift it. Where a drawdown breach holds the same account, the lock names the drawdown. No account a partner trades is on that path yet.

2026-09-25: Two more risk holds

  • A 423 risk_locked, and the order lane’s 409, can name two more holds in params.holds: stop_out_latch, a stop-out the account’s risk decisions recorded, which stays until the account is shown recovered on current prices; and risk_uncovered, risk decisions that do not cover the account’s current state, because a price it depends on is stale, interrupted or cannot be shown complete, or because the decisions have not kept up with its changes. Each refuses new or increased exposure; cancels, exits and reductions stay available. They hold only accounts on the event-driven risk path of the paper book, which no account a partner trades is on yet.

2026-09-25: The saved stop-out ratio is retired (PREVIEW)

stopOutEquityRatio is removed from VenueRiskPolicy in the venue conditions routes, a PREVIEW group. It was saved and never applied to trading; a stop-out is now a threshold of a versioned account risk policy. A profile, group override or account override that still names it is refused as invalid_request, and it is gone from every stored profile and override, whose other values are unchanged. Effective conditions no longer report it.

2026-09-25: Contract and risk policy refusal codes join the catalogue

The Refusal catalogue adds eight codes the paper book will answer with once it binds every order to a versioned contract and every account to a versioned risk policy. No route answers with them yet; a client that translates refusals can add them now, and keeps its generic message for a code it does not know.
  • continuous_symbol_not_contract (instrument, root): a continuous chart symbol names a product, not the dated contract an order trades.
  • contract_unregistered (instrument): no registered contract version has that name.
  • contract_not_trading (instrument, phase, lastTradeAt): the contract is not_listed, close_only before its last trade, or expired.
  • margin_window_closing (instrument, overnightAt, overnightInitial, overnightMaintenance, asset): new futures exposure closes before the day margin window ends, and the params state the overnight requirement per contract a held position meets from overnightAt.
  • risk_policy_unbound (instrument): the account’s collateral pool is bound to no published risk policy.
  • risk_terms_missing, risk_terms_contradictory (instrument, term) and risk_terms_stale (the same and effectiveUntil): a term the policy must state for the contract is absent, out of its effective interval or in conflict with the contract or the account.

2026-09-25: A bare futures root names one safe dated contract, or is refused

A Rithmic order on a bare root (ZN, ES) now goes to the dated contract the product’s own exchange rule names, not one calendar shared by every product. Equity index roots keep the third Friday rule; Euro FX follows its last trade two business days before the third Wednesday; the Treasury roots follow first intention day, two business days before the delivery month, so a bare Treasury root never names a contract in its delivery period. Business days come from the served CME calendar, and a date it does not cover is never assumed.
  • Three trading refusal codes join the catalogue. bare_root_unresolved refuses a bare root that names no one safe contract: its cause is dates_unmodeled, dates_uncovered, unsafe_window or ambiguous (the account holds the root in another contract), with the rule’s contract, the window and boundary it is inside, the specification source and the connection’s delivery cutoffTerm. contract_past_safe_window refuses an order adding exposure to a dated contract inside its unsafe window, and GET /api/trading/actions states it for a position held there. contract_dates_uncovered refuses such an order when the calendar cannot place the window.
  • A dated contract (ZNZ6) is always sent as itself. A close, a flatten, a protective exit, a cancel or an amend keeps the position’s or the order’s own contract after the rule rolls.
  • On the paper book the held-lane window after a roll now ends a day after the rolled-away contract’s own last trade, and futures_contract_unknown gives no_roll_rule when the calendar cannot date the contract.

2026-09-25: A staged account that cannot prove its stage version is close-only, and an issued account’s reset is audited

Breaking. A reset of an issued account now requires a reason and a settled book, and this entry moves the compatibility baseline. No client or integrator used the route, so there is no compatibility path.
  • A new refusal code, stage_version_unrecorded, with params.reason (no_record, or activated_after_opening when the rules in force came into force after the running cycle opened and it has traded) and params.stageId. It answers 423 on an order that would open or add exposure on an account whose group runs a stage and whose running cycle cannot prove the stage version it is judged by. Cancels, exits and reductions stay available, and GET /api/trading/actions states the same: open refused, on the instrument and across the account, and protect, reduce, flatten and cancel allowed where the position allows them. Normal trading returns when the version is established or the issuer resets the account.
  • POST /api/partner/accounts/reset requires comment, the reason for the reset, recorded with it on the balance ledger with the firm as actor and the instant; without it the answer is 400 with code: reason_required. The venue twin, POST /api/partner/venues/{venueId}/accounts/{accountId}/reset, requires comment as well (Preview) and records the acting member or key.
  • A reset of an issued account is refused with 409 and a code, moving and recording nothing, while the account holds a position, a working order or a command whose outcome is unknown (not_settled), has an unresolved liquidation incident or a drawdown breach it has not been flattened from (risk_unresolved), or has a negative balance (deficit_unreconciled). A reset never closes a position or cancels a working order: close them first. A breach the ended cycle recorded stays on record under that cycle.
  • An account a firm or venue issued is reset only by its issuer: the trader’s own Demo reset, POST /api/brokers/paper/reset, answers 403 with code: issued_account for it.

2026-09-25: Balance amounts name their currency

Every Legacy Partner API and venue answer that states an account’s money now names the currency it is in, so a USDC amount is never read as a USD one.
  • The balance.recorded event’s data adds currency, the account’s currency that amount is in.
  • POST /api/partner/balance-op and its venue twin answer with currency; the receipts, GET /api/partner/balance-op and GET /api/partner/venues/{venueId}/balance-ops/{referenceId}, carry the currency the ledger row records.
  • The five risk controls, on GET and PUT /api/partner/accounts/risk and the venue risk-controls routes, add currency, which the loss and profit values are in.
  • A venue’s issue answer adds currency, and the venue stage eligibility schema now documents the currency its facts already carried.
  • GET /api/partner/usage and its venue twin add currency: "USD": trdrs bills in USD, and amountCents and priceCentsPerActiveAccount are cents of it, whatever currency the firm’s paper accounts are held in.
  • A balance operation whose amount has more decimals than the account’s currency states money at, two for USD and two for USDC on the paper book, is refused as 400 with the new code amount_precision_exceeded and params naming the currency and its decimals, before anything is recorded; the same reference can be sent again with the amount corrected. This code and currency_not_issuable are partner and admin errors in the { error, code, params } shape, listed on Errors, not trading refusals.

2026-09-25: Refusals carry a code and parameters, and an account says what it may do

Every refused trading request now carries a stable code and its params beside error, which stays the English fallback. The codes are one catalogue, the Refusal model of the API reference: the account-mode codes of a Binance, Bybit or Hyperliquid account, the paper book’s codes of the entries below (product_unproven names the instrument, the account currency and the products proven for it), unknown_instrument and asset_class_not_supported from the asset-class routing, an issued account’s venue conditions (entry_halted, instrument_not_permitted, order_quantity_above_limit and the rest), risk_locked with the account’s holds in params.holds, feature_disabled, the engine’s own admission codes, and order_rejected for any other rejection. params never carries a secret, an account number or text a provider sent. Errors lists what each code carries.
  • GET /api/trading/actions returns whether the account may open or add exposure (open), reduce a held position through the order route (reduce), close part of it through the close route (close), protect it (protect), flatten it (flatten) and cancel (cancel), on one instrument or across the account, with the refusal each refused action would get. Each verdict runs its own route’s standing checks: under a hold reduce is refused as the order route refuses it, and close is refused unless the provider enforces it as reduce-only. It is advisory: the order is judged again when it is sent. Requires a Trading API key.
  • Each accountMode.refusals entry on the account snapshot adds params.
  • A 423 risk_locked adds params.holds: trading_lock, drawdown_latch, liquidation_incident, or null when the holds could not be read.
  • A 403 feature_disabled names the switched-off feature in params.feature.
  • On an issued paper-book account, a resting order or bracket whose venue terms are missing or disagree with the paper book is refused when it is placed (venue_terms_missing, venue_terms_mismatch), as an immediate order already was, instead of resting until the matcher rejected it.

2026-09-25: A cycle is judged by the rules it opened under

Stage eligibility, advancement and the drawdown rule judge an account’s running cycle by the stage version it recorded when it opened: its profit target, trading days and the session they are counted in, disqualifying rules and drawdown. A paper-book account records the version in force when it is placed or moved into a group that runs the stage, when it resets and when an advance issues it. Activating a version with new rules takes effect at once, even while accounts running the stage hold positions, and reaches each account at its next cycle. Where a passing account lands, nextGroupId, is routing: it comes from the version in force at the advance, so a change of destination alone applies at once. Preview groups only.
  • The eligibility response adds economicHash, routingPolicyId and routingActivationRevision. policyId, activationRevision and economicHash name the version that judges the cycle; the routing fields name the version in force now. An advance records both and is refused with version_conflict if either has moved since it was decided.
  • Activating a stage version no longer waits for the stage’s accounts to be flat, and no longer answers 409 not_quiescent.
  • For an account on the venue ledger, eligibility counts only the running cycle: its trading P&L, trading days and recorded stop-outs start at its opening allocation, and its cycleId names that allocation, so a reset account is judged on what it did since the reset.
  • A cycle with no record of the rules it opened under answers 409 not_eligible with stage_version_unrecorded, both for the eligibility read and for an advance, rather than being judged by the version now in force, and the drawdown rule does not watch it. That is a paper-book account moved into the stage part-way through a traded cycle after the rules now in force came into force, until it resets, and every account held at a provider, whose eligibility read previously listed the missing evidence.

2026-09-25: Issue a paper-book account in USDC

Create evaluation accounts accepts currency on each item, USD or USDC, and USD when it is omitted, which is what every account issued before the field existed is held in. The currency is fixed when the account opens; no existing account changes currency.
  • USDC accounts are issued in the sandbox only, for the sandbox partner flow. Any other deployment refuses a batch naming USDC as 400 with the new code currency_not_issuable and params naming the currency and the environment, and creates nothing.
  • A USDC account’s money is stated in two decimals, a disclosed paper-book rule and not USDC’s native six-decimal precision. Each fill’s profit and loss is rounded to the hundredth, so rounding can accumulate across fills.
  • startingBalance stays a whole number of that currency, 1000 to 10000000, as on the venue route. An account may later hold cents from trading.
  • Each result carries currency: the item’s on a fresh create, the account’s own on a re-answered referenceId.
  • A USDC account trades HYPERLIQUID:BTC, a Hyperliquid core perpetual that settles in USDC, the one product proven on a USDC account so far. Its fills, profit and loss, balance, balance operations, stage checks and resets are in USDC. Every other instrument is refused with the new code product_unproven, and its account instruments list only HYPERLIQUID:BTC.
  • A USD account still refuses every registered perpetual as settlement_asset_unmodeled. Its account instruments no longer list any perpetual, since none of them settles in USD.
  • Stage eligibility adds currency, the account currency its netTradingPnl and profitTarget are stated in, as firm analytics already did.
  • The venue route POST /api/partner/venues/{venueId}/accounts issues USD accounts only: a body naming currency is refused as 400 invalid_request, and a USDC account is never placed into a venue group.

2026-09-25: The paper book refuses what it cannot price honestly

The paper book, under a trader’s own Demo and every account a venue issues, now refuses orders and reports values as unknown where the contract, the multiplier, the price or the settlement asset is not reliable. Each refusal is a 400 or 422 with its code, sends and books nothing, and releases the idempotency claim.
  • futures_exposure_refused: a futures position on the paper book is keyed by its root, not by a dated contract, so every futures order that would open or add exposure is refused, resting and bracket orders included. An existing position can still be closed, and protected with exits, while the contract it was opened on is the one priced. After the engine’s roll it is refused with futures_contract_unknown and its unrealizedPnl is null; it is never priced from the next contract. So is a position opened in the nine days after a roll, while the rolled-away contract could still have priced it.
  • instrument_not_tradable: CL, MCL, NG, GC, MGC and SI. They leave the paper book’s account instruments.
  • unknown_instrument: a symbol with no contract specification on the paper book, including one only the chart feed serves. Charting it is unchanged.
  • market_data_stale, market_data_unavailable, market_data_delayed: a trade or mark older than 10 seconds by its own time, no price, or a delayed feed outside the sandbox. A sandbox Rithmic Test login still prices practice trades at its true, delayed, age; symbol metadata labels it dataStatus: delayed_streaming.
  • settlement_asset_unmodeled: a registered perpetual on an issued account not held in the asset it settles in, and futures on an account not held in USD. The paper book’s registered perpetuals are the Hyperliquid core perpetuals, which settle in USDC, and the Binance and Bybit USDT-quoted perpetuals, which settle in USDT; a builder-deployed Hyperliquid market is not registered and is refused as unknown_instrument. A Personal Demo settles a registered perpetual’s profit and loss as synthetic USD at a fixed 1:1 parity and holds no stablecoin.
  • venue_terms_mismatch and venue_terms_missing: an issued account’s venue instrument whose multiplier, tick, quantity unit, product model, settlement currency or commission currency disagrees with the paper book, or an entry with no venue terms. A dated listing no longer admits an order the paper book keys on its root.
Firm analytics positions add openedAt, the time the account’s own fills say the position opened (null when that cannot be established).

2026-09-24: Risk events carry an event id and a revision

risk.locked and risk.unlocked deliveries add two fields to data: eventId, which is the same on every delivery and retry of one lock change, and revision, which increases with each lock change on the account. Each event is recorded in the transaction that changes the lock, so a restart cannot drop one. Existing fields are unchanged. Receive events explains deduplication and ordering.

2026-09-23: Venue risk book (preview)

  • GET /api/partner/venues/{venueId}/risk and GET /api/operator/venues/{venueId}/risk read every paper-book customer account the venue issued, up to 500, as one book at one instant (asOf): totals, exposure by instrument with long, short, net and gross quantity and notional, every open position and working order, and each account’s figures. Requires account:read.
  • A total is stated only when every included account answered in one currency; a total a missing mark would change is null. Notional uses a mark no older than 10 seconds.
  • The hedging section lists the hedge engine’s current targets, policies, open incidents and execution metrics beside customer exposure, never netted into it. Without hedge:read it reads state: unavailable and the rest of the book is still returned.
  • history aggregates the per-account equity samples per minute bucket over historyWindow and historyInterval. A point carries a total only when every account in scope was sampled in that bucket; otherwise it says why.

2026-09-22: Venue team membership

An owner can now manage who works on an organization’s venues from a signed-in session. Each person keeps their own trdrs sign-in; no password or Venue key is shared.
  • GET /api/operator/organizations/{organizationId}/members returns each member’s email.
  • POST /api/operator/organizations/{organizationId}/members accepts email as an alternative to authUserId. An address without a verified account creates a pending invitation and receives email. It grants no access until that exact address completes verified sign-in, when the role activates automatically.
  • Pending invitations are returned with pending: true and may be changed or cancelled by email.
  • POST and DELETE on the same path are in the reference, with their version, last-owner and idempotency behavior.
Venues can now open the same short-lived Connect flow from the trdrs app, an installed back office, or their own website. Exact website origins are registered before a backend may mint a session. The venue hedging preview keeps customer execution separate from the venue-owned hedge account and routes every hedge command through the existing per-account execution coordinator.
  • Connect setup and backend sessions: /api/operator/venues/{venueId}/connect/origins, /api/partner/venues/{venueId}/connect/sessions, and /api/partner/venues/{venueId}/connect/sessions/{sessionId}.
  • Browser handoff: /api/connect-link/mount, /api/connect-link/sessions/{sessionId}/claim, /api/connect-link/sessions/{sessionId}/complete, and /api/connect-link/sessions/{sessionId}.
  • Partner hedge controls: /api/partner/venues/{venueId}/hedges, /api/partner/venues/{venueId}/hedges/policies, /api/partner/venues/{venueId}/hedges/activation, /api/partner/venues/{venueId}/hedges/calculate, /api/partner/venues/{venueId}/hedges/intents/{intentId}/dispatch, /api/partner/venues/{venueId}/hedges/intents/{intentId}/reconcile, /api/partner/venues/{venueId}/hedges/emergency-stop, and /api/partner/venues/{venueId}/hedges/flatten.
  • The signed-in operator twins use /api/operator/venues/{venueId}/hedges, /api/operator/venues/{venueId}/hedges/policies, /api/operator/venues/{venueId}/hedges/activation, /api/operator/venues/{venueId}/hedges/calculate, /api/operator/venues/{venueId}/hedges/intents/{intentId}/dispatch, /api/operator/venues/{venueId}/hedges/intents/{intentId}/reconcile, /api/operator/venues/{venueId}/hedges/emergency-stop, and /api/operator/venues/{venueId}/hedges/flatten.

2026-09-21: Venue branding and instrument reference data

An installed back office can now manage its venue’s ongoing brand and populate instrument forms with one scoped Venue key. The operator-session twins use the same handlers and checks. These routes do not change listing approval, company roles, referral links, accounts or live trading.
  • GET/PUT /api/partner/venues/{venueId}/brand and GET/PUT /api/operator/venues/{venueId}/brand read and update the venue’s name and description (venue:read, venue:configure). Writes include the updatedAt value from the preceding read; a stale write returns version_conflict instead of overwriting a later change.
  • POST /api/partner/venues/{venueId}/brand/logo and POST /api/operator/venues/{venueId}/brand/logo validate, sanitize and store an engine-hosted PNG (venue:configure) with the same optimistic timestamp check.
  • GET /api/partner/venues/{venueId}/reference/instruments and GET /api/operator/venues/{venueId}/reference/instruments return the static contract reference catalog (venue:read). The read grants no market-data entitlement and activates no venue instrument.

2026-09-19: Stages and analytics on accounts held at a provider

Stage eligibility and the account analytics now answer for an account a venue holds at a provider, from the provider’s own records of its fills, instead of permission_denied. Nothing is written and no second set of books is kept. Preview groups only.
  • GET /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/eligibility measures a provider-held account from the provider’s records. observedRules is empty for such an account, so a policy naming a disqualifying rule answers unobserved_disqualifying_rule.
  • GET /api/partner/venues/{venueId}/accounts/{accountId}/analytics and the /api/operator/… twin answer for a provider-held account with book: provider, no equity history and no rules.
  • The venue read carries level2.appliesAtProvider beside applies: for an account held at a provider only stages is applied; its orders and fills are the provider’s own.
  • The advance on a provider-held account still answers permission_denied: the successor would have to be created at the provider.

2026-09-19: One key for a venue backend

The four things a venue’s backend still needed the Legacy Partner key for now exist on the venue routes: the five per-account risk controls, usage, the receipt of one balance operation, and webhooks. Each runs the same operation the legacy route runs, behind a venue scope, under both families. A webhook registered through either is the same row and fires for the same events. The legacy routes are unchanged and stay until every firm portal has moved. Preview groups only; nothing on the market, trading or Partner routes changed.
  • GET/POST /api/partner/venues/{venueId}/accounts/{accountId}/risk-controls and GET/POST /api/operator/venues/{venueId}/accounts/{accountId}/risk-controls: read and replace the five per-account controls (account:read, risk:halt).
  • GET /api/partner/venues/{venueId}/usage and GET /api/operator/venues/{venueId}/usage: the venue’s metered usage for a month (venue:read).
  • GET /api/partner/venues/{venueId}/usage/accounts and GET /api/operator/venues/{venueId}/usage/accounts: the accounts behind that number (venue:read).
  • GET /api/partner/venues/{venueId}/balance-ops/{referenceId} and GET /api/operator/venues/{venueId}/balance-ops/{referenceId}: the durable result of one balance operation by the Idempotency-Key of its write (account:read).
  • GET/POST /api/partner/venues/{venueId}/webhooks and GET/POST /api/operator/venues/{venueId}/webhooks: list and register webhooks (venue:read, venue:configure).
  • DELETE /api/partner/venues/{venueId}/webhooks/{webhookId} and DELETE /api/operator/venues/{venueId}/webhooks/{webhookId}: remove one (venue:configure).
  • POST /api/partner/venues/{venueId}/webhooks/{webhookId}/test and POST /api/operator/venues/{venueId}/webhooks/{webhookId}/test: send a signed test ping (venue:configure).
  • GET /api/partner/venues/{venueId}/webhooks/{webhookId}/deliveries and GET /api/operator/venues/{venueId}/webhooks/{webhookId}/deliveries: the delivery log (venue:read).

2026-09-18: Level 2 on the paper book, one record per company, two switches

The venue’s rules now govern the accounts it issues on the paper book: an order is asked the venue’s conditions before it is claimed, a fill takes the venue’s markup inside the route’s collar and the venue’s commission, a stage check reads the account’s own fills, and a drawdown rule on the stage policy flattens and locks on a breach. Connect and every venue’s Providers list are drawn from one record per company. Preview groups only; nothing on the market, trading or Partner routes changed.
  • The venue read carries level2: { enabled, applies } and company.
  • POST /api/partner/venues/{venueId}/level2 and POST /api/operator/venues/{venueId}/level2 turn level 2 on or off. Creating the first group turns it on.
  • POST /api/partner/venues/{venueId}/connections/{connectionId}/expose and POST /api/operator/venues/{venueId}/connections/{connectionId}/expose show or hide a plugged-in provider to the venue’s own traders. Off by default.
  • GET .../providers under both families carries available, the companies a venue may plug in.
  • A stage policy may carry drawdown; the lock reasons max_drawdown and daily_drawdown join the account snapshot. Both permanent, like eval_breach.
  • The customer ledger read answers 404 for an account the venue issued on the paper book: its books are the paper book’s, and the analytics route on the account serves them.

2026-09-18: One structure, one glossary

Documentation only; no route changed. The site now uses one word per thing: venue, group, provider (built-in or public), login style, paper book, Demo, Connect, listed, level 2, Venue key, Trading API key, back office. Start Here opens with How trdrs fits together, and Build is filed by layer: trading platform, Connect, venue, providers, the Legacy Partner API, and the API standards. The Venue keys, Venue accounts and Get listed guides are new; the two onboarding orphans, the Connect and webhooks concept pages, the account-paths page and the two overview pages were merged into the guides that carry them, with redirects. The OpenAPI descriptions took the same words; tags, paths, parameters and schemas are unchanged.

2026-09-18: Every provider declares how it is signed into

A provider’s login style is now part of its declaration, and Connect reads it instead of knowing each provider by hand. The six styles are the trader’s own login, OAuth, API key, wallet key, the firm’s credentials, and none.
  • /api/connect/providers lists the six built-in providers (Rithmic, Tastytrade, Hyperliquid, Binance, Bybit, the trdrs paper book) with the loginStyle each declares and whether a trader login belongs to a named system. Keyless and static, like the Connect firm list.
  • Every row of the Connect firm list (/api/connect/firms) carries loginStyle, derived from the provider the tile opens.
  • A public provider’s manifest may declare loginStyle. Omitted, it reads as firm_credentials: the venue connects the provider with the firm’s own credentials and the trader types nothing. Every manifest written before the field validates unchanged.

2026-09-16: The firm’s accounts through the venue routes

Still the private preview: these are served on the sandbox. The venue API is now the one API a firm’s backend needs. The operations the legacy Partner API has always done on a firm’s accounts are reached through the venue, under both families, each behind a named venue-key scope, and each running the SAME operation the legacy route runs: same ledger-first ordering, same idempotency rule, same refusals. The legacy /api/partner/accounts… and /api/partner/balance-op routes stay and are marked legacy in the reference, with their venue twin named. Run firm accounts maps each call to its twin, and Venue accounts walks the twins in order.
  • /api/partner/venues/{venueId}/accounts lists the venue’s accounts (account:read) and issues one evaluation account into a group (account:issue). The group must have a route, else 409 group_has_no_route before anything is issued; a venue with no adopted firm is 404 no_firm. /api/operator/venues/{venueId}/accounts is the session route family onto the same.
  • /api/partner/venues/{venueId}/accounts/{accountId}/balance credits, debits or adjusts (balance:write). The Idempotency-Key is the firm’s referenceId; a repeat is 409 duplicate_reference. Also /api/operator/venues/{venueId}/accounts/{accountId}/balance.
  • /api/partner/venues/{venueId}/accounts/{accountId}/reset resets to the starting balance under the new account:reset scope. Also /api/operator/venues/{venueId}/accounts/{accountId}/reset.
  • /api/partner/venues/{venueId}/accounts/{accountId}/halt and /api/partner/venues/{venueId}/accounts/{accountId}/resume (risk:halt), with a lock the firm does not own answered as 409 lock_conflict. Also /api/operator/venues/{venueId}/accounts/{accountId}/halt and /api/operator/venues/{venueId}/accounts/{accountId}/resume.
  • /api/partner/venues/{venueId}/accounts/{accountId}/analytics reads the account’s trading analytics (account:read), measured as the legacy analytics route measures them. Also /api/operator/venues/{venueId}/accounts/{accountId}/analytics.
  • Venue key scopes gain account:reset.
  • The firm-operations half of the Partner API is being replaced by the venue API, group by group; Connect stays as the trader-facing module’s own API. Run firm accounts carries the status of each group and its venue twin; a group is marked legacy once the twin exists.

2026-09-16: The venue routes through a browser session

Still the private preview: these are served on the sandbox. Every venue configuration call is reached two ways, and the reference now lists both. A venue key authenticates a venue’s own backend at /api/partner/venues/…; a signed-in owner reaches the same services at /api/operator/venues/… with a session cookie and a trusted Origin. Same validation, same compare-and-swap, same refusals. A browser never holds a venue key, which is why the session route family exists. Venue keys explains the split.
  • /api/operator/organizations lists and creates the organization that owns venues. A new one gets a non-login owning principal and the caller gets owner membership.
  • /api/operator/organizations/{organizationId}/venues lists that organization’s venues with the environment each belongs to, so nothing has to paste a venue UUID.
  • /api/operator/organizations/{organizationId}/members lists who may operate them.
  • /api/operator/venues creates a venue. environment must equal the environment the engine serves: an engine serves exactly one and refuses a body naming the other.
  • /api/operator/venues/{venueId} reads one.
  • /api/operator/venues/{venueId}/firm adopts the caller’s firm into that venue, with no body. It moves nothing: accounts the firm already issued keep the conditions they were issued under. firm_already_linked and venue_already_linked are 409; a caller with no firm is 404 no_firm.

2026-09-14: Venue platform private preview (not publicly enabled)

Preview: served on the sandbox to every venue; production availability is arranged when a venue qualifies. Existing trdrs_sk_… keys are unchanged; the new scoped venue credential is trdrs_vk_sandbox_… or trdrs_vk_production_….
  • /api/operator/venues/{venueId}/keys creates and lists keys through a verified owner session. /api/operator/venues/{venueId}/keys/{keyId} revokes them. Bearer keys cannot manage keys.
  • /api/operator/venues/{venueId}/connections/{connectionId}/accounts lists saved discovery without provider authorization references. Expired results and stale cursors require fresh validation.
  • /api/operator/venues/{venueId}/accounts/bind binds a selected discovered account using its provider permissions. Retries recover the original binding; no trader grant, balance or trading readiness is created. Account-capable validation now acquires a session and saves complete discovery.
  • Provider setup is served under both families, with identical checks and different credentials: a verified owner session reaches /api/operator/venues/{venueId}/providers, /api/operator/venues/{venueId}/connections, /api/operator/venues/{venueId}/connections/{connectionId}/validate and /api/operator/venues/{venueId}/connections/{connectionId}/validations/{jobId}; a venue’s own backend reaches /api/partner/venues/{venueId}/providers, /api/partner/venues/{venueId}/connections, /api/partner/venues/{venueId}/connections/{connectionId}/validate and /api/partner/venues/{venueId}/connections/{connectionId}/validations/{jobId} with the provider:manage, connection:manage and venue:read scopes. Registering a provider contacts no endpoint. Creating a connection encrypts the credential and returns metadata only. Validation is a queued read-only job: the initiating key is rechecked before the fetch and again before the result commits, so revoked keys and lost ownership fail the job rather than finish it. A pass never clears a halt or makes the connection trading-ready.
  • /api/partner/venues/{venueId} reads the key’s venue with venue:read.
  • /api/partner/venues/{venueId}/instruments and /api/partner/venues/{venueId}/instruments/{candidateId} save/read immutable instrument candidates. Candidates do not activate a trading catalog.
  • /api/partner/venues/{venueId}/instruments/active lists and sets the version a venue dispatches against, with venue:read and venue:configure. Activation is a compare-and-swap on expectedRevision (null means no activation exists yet), and the version must keep a mapping on a live connection. A rename or an entry halt applies immediately; any other change is economic and requires every account bound to the venue to be provably flat in that instrument, with the blocking account IDs returned on refusal. Activating creates no grant, balance or trading readiness.
  • Venue conditions decide what an account is charged and what it may do. /api/partner/venues/{venueId}/conditions/profiles publishes immutable profiles and /api/partner/venues/{venueId}/conditions/active applies one, by compare-and-swap. /api/partner/venues/{venueId}/conditions/groups defines flat account groups, /api/partner/venues/{venueId}/conditions/accounts/{accountId}/group moves an account between them, /api/partner/venues/{venueId}/conditions/accounts/{accountId}/risk sets per-account safety and /api/partner/venues/{venueId}/conditions/accounts/{accountId}/effective reads the resolved answer with the layer that decided each value. Economics resolve by specificity and stop at the group; safety resolves by intersection, so a more specific layer can only ever tighten. A change that only tightens safety, or only renames, applies at once; a change to margin, commission, markup or financing requires the affected accounts to be provably flat and returns not_quiescent with the blocking account IDs. Orders that add exposure are refused against these limits; reducing orders are not, so an account can always get out. /api/partner/venues/{venueId}/conditions/preview answers whether a proposed profile or group override changes economics at all, how many accounts it reaches and which are not ready, before the change is attempted rather than only when one is refused. It writes nothing.
  • /api/partner/venues/{venueId}/routes defines where a group’s orders go, internal or external, and /api/partner/venues/{venueId}/groups/{groupId}/route points a group at one. An external route names both its connection and its dedicated account; a half-configured route is refused at save. Moving a route’s mode, connection or account waits for the accounts on it to be flat. Retiring one stops it being offered to new groups and keeps it serving the ones already on it. There is no default route: an account whose group names none cannot open new exposure, though it can always close what it holds. A route also carries the collar every order sent through it has to fill inside - a count of the instrument’s ticks, or a share of the quote in basis points - along with the fee it reserves per unit and whether it may run an uncapped market order at all. An omitted collar is zero adverse ticks, which is the strictest reading and not “no collar”, and uncappedMarketAllowed defaults to false and is refused on an internal route, where there is no provider cap to defer to. Widening a collar waits for quiescence like a mode change, because it changes the worst case of a trade already in flight. A buy is collared off the ask and a sell off the bid; the venue’s own markup is measured inside the same collar, so a markup reaching past it refuses the fill rather than charging more than the stated protection allows.
  • The whole venue configuration surface is served under /api/operator/venues/{venueId}/... as well, to a verified owner session instead of a venue key. A browser must never hold a venue key, so an operator console has no way to reach the partner family; this is how it reaches the same routes. They are the same router with a different credential rather than a second copy, so the two cannot drift into two permission models. The session path additionally requires a verified email, and a caller who is not a member of the venue’s organization receives 404 rather than 403, which would confirm the venue exists to somebody with no business knowing. A bearer key presented to the operator family is refused outright.
  • /api/partner/venues/{venueId}/accounts/{accountId}/instruments reads the venue’s activated catalog resolved against one account: its conditions, its route and its open liquidation. A blocked instrument is returned with its reasons rather than omitted, so a trader who cannot find what was there yesterday can be told whether it was a halt, an allowlist, a retired route or a missing provider mapping. Every reason is an entry gate: a halted or de-listed account can still close what it holds. It answers configuration only: it cannot see a price, a balance or a stream, so basis is always account_configuration_only and an empty blockedBy is not a promise that an order will be accepted. An account belonging to another venue is refused rather than answered empty. /api/partner/venues/{venueId}/accounts/{accountId}/ledger reads a customer account’s balance, held collateral, positions and route, and /api/partner/venues/{venueId}/accounts/{accountId}/incident reads its open liquidation with the reduction steps recorded against it. Both are scoped to the calling key’s venue, and the ledger exists only for accounts whose balance authority is TRDRS.
  • /api/partner/venues/{venueId}/stages/policies publishes immutable stage rules and /api/partner/venues/{venueId}/stages/active puts one in force, both with stage:advance. A stage rule is stored rather than supplied with the advancement request: a target that arrives with the question can be made easier by whoever asks it. It carries the profit target, the minimum trading days, the session timezone and rollover, the recorded risk breaches that disqualify a cycle, and the group a passing account lands in. A session timezone the runtime cannot resolve is refused at publication rather than at the first trading-day boundary. Moving the target waits for every account running that stage to be provably flat; changing only where a passing account lands applies at once, because it decides the next account rather than this one. /api/partner/venues/{venueId}/conditions/groups now carries an optional stageId, and moving a group onto a different stage is an economic change for the same reason.
  • /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/eligibility reads whether an account has passed, and /api/partner/venues/{venueId}/accounts/{accountId}/stages/{stageId}/advance issues its successor. The decision is MEASURED from the account’s own books against the version in force, never supplied by the caller: a partner able to state its own net trading P&L could issue itself a funded account by describing one. Net trading P&L excludes allocations and balance operations and is taken after all fill fees; trading days are counted from when each fill actually traded rather than when it was booked, so a replay after an outage does not hand an account a day it did not trade; a fill whose source never said when it happened makes the window incomplete rather than counting as today. reasons lists every failing condition at once. The advance issues one successor per source cycle and stage - a second attempt with a different idempotency key returns the account the first one made - and it re-checks the version, the account’s revision and its quiescence before creating anything. startingAllocation is required and null is a real value meaning a zero-balance successor. Only for accounts whose balance authority is TRDRS. A refusal is 409 not_eligible with its reasons.
  • /api/partner/venues/{venueId}/accounts/{accountId}/grants issues an invitation for an already bound account. /api/partner/venues/{venueId}/accounts/{accountId}/grants/{claimId} reads or revokes it. grant:manage and account:read are separate permissions. Email alone never grants trading access, and accepting an invitation does not make a provider connection ready.
All key-authorized operations recheck venue, environment, scope, expiry, revocation and current issuer ownership in their database transaction. No scope provides raw trader authority.

2026-09-12: Firm analytics and stage eligibility

GET /api/partner/accounts/analytics exposes current-cycle fill-derived net trading profit, fees, session-based trading days, closed trades, open positions/orders, sampled equity, reset boundaries and explicit completeness. Partner keys reach only accounts their firm issued. GET /api/partner/accounts/eligibility checks a firm’s target and minimum days against complete records, a flat account with no working orders/pending commands, no recorded disqualifying breach, and a firm halt. It returns a stable reference for retrying next-stage account creation; it does not advance or reset accounts. Legacy cycles with unknown risk history cannot auto-pass. See Account analytics and stage eligibility.

2026-09-11: Sandbox firm branding

Set your firm’s name, description and PNG logo on the sandbox developer page before conformance. GET /api/account/list includes the provisioning firm’s identity for authorized accounts; trading routes continue to use paper. See sandbox branding.

2026-09-11

  • Free sandbox integration self-checks. POST /api/partner/conformance/runs opens a two-hour run, GET /api/partner/conformance/runs/{id} returns measured results, and POST /api/partner/conformance/runs/{id}/close finalizes them. The sandbox observes the firm’s developer keys and tests handling of 429, 423 and a dropped stream. Other firms, browser sessions and traffic outside an open run are unaffected. Runs and evidence survive restart. No payment configuration is required. See Run a self-check.

2026-09-08

  • An account says which symbols it can trade. GET /api/account/instruments returns the symbols the resolved account can trade through this engine, one row per tradable symbol with the search catalog’s own display identity (symbol, name, exchange, type), plus a label for the account: the firm or system name where the engine stores one (Apex), else the account number. The set is the venue’s own: a futures account lists the contract catalog, a crypto venue account lists that venue’s perpetuals, and the demo lists both. Additive, and it takes the same broker/account selectors and answers the same errors as the other account reads.
Breaking. The word “claim” left the wire, and this entry moves the compatibility baseline. It lands before the first partner integration, which is why it lands at all; see Stability.
  • Connect speaks registration. On /api/partner/connect/accounts: the response fields claim and claims are now registration and registrations, claimedAt is linkedAt, and the status claimed is linked (the full set is pending, linked, revoked, expired). The schema names follow (PartnerRegistration*). Routes are unchanged.
  • Webhook event types follow. claim.claimed is now registration.linked and claim.revoked is registration.revoked; their payloads carry registrationId instead of claimId. The other event types are unchanged, and an endpoint registered with an empty filter receives the new names automatically.

2026-09-07

Breaking. The exit-plan placement shape changed, and this entry moves the compatibility baseline. There is no shim and no dual delivery. It lands before the first partner integration, which is why it lands at all; see Stability.
  • Protection is engine state, and it rides the account revision. GET /api/account/snapshot and GET /api/account/stream now carry brackets and managedExits: every bracket the engine holds with its legs and their states, and every breakeven or trailing stop it is running, with the phase reached and the reason it is paused where it is. Both lanes carry the same revision as the rest of the account, so a protective change and the order change that caused it are points on one line. GET /api/account/brackets and GET /api/account/managed-exits serve the same two lanes for a surface that wants only one of them; a client holding a snapshot already has them. A leg in pre_armed is recorded and priced but deliberately not resting yet, because the position it protects does not exist; arming it also RE-PARENTS it, so a leg protecting a position carries a null parentOrderId.
  • Saved exit plans are a principal-scoped catalog with conditional writes. GET /api/exit-plans lists the plans a trader holds and POST /api/exit-plans authors one. PUT /api/exit-plans/{id} and DELETE /api/exit-plans/{id} are CONDITIONAL: send back the opaque revision you read, and a stale one is refused with 409 and the revision the plan carries now, rather than overwriting an edit that arrived in between. A revision is a digest, not a counter, so never construct one. Each plan reports the capabilities it requires, so a host can tell which plans a selected account can run before offering them.
  • A plan is placed by reference, and the engine resolves its legs. POST /api/account/exit-plans/preview prices the ladder from the account’s own instrument facts, says what it protects and what the order carries that it does not, and returns a token binding the ORDER: the account, the plan and its revision, the instrument, the side, the quantity and the entry. POST /api/trading/order then takes exitPlan: { planId, planRevision, previewToken } and no levels at all: the engine reloads the plan at that revision, re-resolves, and refuses an edited plan, a changed account, drift or an expiry. The resolved-leg request shape it replaces is gone, along with the browser arithmetic it carried.
  • The futures catalog states its fractional roots in executable form. A row of GET /api/instruments for a product the exchange quotes in thirty-seconds now carries priceFraction: denominator, the number of parts one point divides into (32, 64 or 128), and subFraction (2 or 4) where the writing divides one of those again. It comes from the same reading of the tick and the quotation convention that writes the fractional price format on GET /api/market/symbol-info, so a ticket sizes and snaps against the served fact rather than deriving a denominator of its own. A product quoted in decimals carries no priceFraction at all.
  • The order warnings are named for the plan. exit_plan_remainder_unplaced replaces atm_remainder_unplaced and exit_plan_not_recorded replaces atm_management_not_registered. Both still mean what they meant: the protected bracket is live either way, and the message says what did not happen.
  • A preview names the order it will be spent on. POST /api/account/exit-plans/preview takes the clientOrderId the placement will ride under, and the token binds it. A preview is therefore good for one submission rather than for any order until it expires. Generate the client order id before you preview and send the same one when you place.
  • The two protective one-shots carry the revision they were read at. GET /api/account/brackets and GET /api/account/managed-exits answer { revision, items }. The number is the account’s current one, read rather than allocated: these reads do not advance the sequence your stream folds.
  • Exit plans are reachable with a tenant key. The catalog and the preview take a tenant key as well as a signed-in session, because a saved plan belongs to a principal and a key is one. A key reaches only its own plans.
  • A capability descriptor says which parts of a plan the account can run. Every account frame’s capabilities carries exitPlanSupport: the subset of stop_loss, take_profit, multiple_targets, runner_leg, breakeven and trailing_stop this account can execute. Compare it against a plan’s requires to offer only the plans that will work.

2026-09-06

Breaking. The account stream changed shape, and this entry moves the compatibility baseline. There is no shim and no dual delivery. It lands before the first partner integration, which is why it lands at all; see Stability.
  • One event carries the whole account. GET /api/account/stream sends one account event, on connect and on every change alike, and it carries the summary, the positions and the working orders as they were at one moment, with the revision that names that moment. The snapshot, positions and orders events are gone. A client that switched on those event names, or that merged them into a picture of its own, must be changed: subscribe to account and replace your whole picture with each frame.
  • Fold by the revision and nothing else. Hold the newest frame you have received for an accountId. Drop a frame at or behind that revision. Replace everything with any frame ahead of it. A frame whose revision skips ahead is still complete, so there is no gap to detect and no resync to perform; a client written to resync on a jump should stop doing so. Never compute a revision of your own.
  • GET /api/account/snapshot serves the same body. Read it when you have no stream open, or when you come back after a disconnect. Combining the one-shot summary, positions and orders reads is no longer the way to build a picture: three reads are three moments, and nothing in them says whether they agree.
  • accountId is how an account is named. It is an opaque token on every frame. Key panels, subscriptions and state by it; it is not a structure and must not be taken apart. An account number is unique at its own firm and nowhere else.
  • capabilities says what the account supports. The frame carries what the broker adapter behind it implements, so you render only controls the venue can accept. A time-in-force absent from tifs is refused, never mapped to a different lifetime.
  • clock says what the revision counts. engine means trdrs owns the account’s state and the number is the account’s own history. observation means the account lives at a venue and the number stamps our reading of it, so the order of readings is exact and the interval is not.
  • positionModel says the book is net. One instrument in one account holds at most one signed position, so a frame carries at most one row per instrument.

2026-08-29

  • Connect makes the firm the only customer messenger. trdrs no longer sends account-ready emails on a firm’s behalf. POST /api/partner/connect/accounts no longer accepts handover, its response no longer includes emailSent, and claim reads no longer include handover. Requests that still send the retired field receive 400; there is no compatibility shim. Firms notify traders and deliver venue credentials through their own secure onboarding process. This contract correction landed before the first partner integration and is the Connect compatibility baseline.
  • The futures contract catalog joins the licensed surface. GET /api/instruments serves one row per futures product root (exchange, tick size and value, dollar multiplier, roll cycle, and the listed months of products whose roll is not modelled), the same static facts the trdrs order ticket sizes, snaps and prices with. Admission is the market-data model: your tenant key or your allowlisted origin.
  • Risk controls join the Account group. GET /api/risk reads an account’s five risk controls (daily and weekly loss limits, daily and weekly profit targets, the end-of-day close) together with its lock state; PUT /api/risk replaces the controls in one write, live from the moment it lands; POST /api/risk/unlock is the manual unlock. These are the controls behind every 423 risk_locked a trading route answers, and the lock object is the same one that streams as the lock event on /api/account/stream, so a front end holds one lock truth. Admission is the trading model exactly: your tenant key, acting as the accounts’ owner. broker is required on all three; account targets one account under a multi-account login.
  • News and economic calendar join the licensed surface. A new News group in API Reference: GET /api/news (aggregated market headlines, newest first, keyset-paged, scoped by futures root with instrument), GET /api/news/stream (SSE: news_item frames), GET /api/news/image/{id} (the keyless thumbnail proxy), GET /api/calendar (scheduled events in a bounded window, with impact tiers and display-string forecast/previous/actual), and GET /api/calendar/stream (SSE: calendar_update nudges). News and the calendar each carry their own live stream. Admission is the market-data model exactly: your tenant key or your allowlisted origin.

2026-08-28

  • Market data: one connection, many charts. GET /api/market/streams multiplexes up to 24 bar subscriptions over a single SSE connection: subs is a comma-separated list of INSTRUMENT~tf tokens, every event payload carries instrument and tf for demultiplexing, and a refused subscription becomes a tagged error event while the rest live on. Built for multi-chart layouts, where browsers cap concurrent connections per origin.
  • Market data: spread expressions are instruments. /api/market/history and the bar streams accept arithmetic over instruments, such as ES-NQ, 1/ESU6, (ES+NQ)/2; operators + - * / ^, parentheses, numeric literals, up to four legs. The engine evaluates server-side over the bucket intersection of the legs; spread bars carry volume 0, and a string that matches a catalog symbol exactly is always that symbol, never arithmetic.

2026-08-24

  • Webhooks: pushed events with signed delivery. A new Webhooks group. Register up to five https:// endpoints per firm with POST /api/partner/webhooks (optionally filtered to specific event types), list them with GET, and remove one with DELETE. Six event types in v1: claim.claimed, claim.revoked, account.reset, balance.recorded, risk.locked, and risk.unlocked. Every delivery is signed (trdrs-signature: HMAC-SHA256 over timestamp.body, so a replayed payload cannot wear a fresh clock) and retried on failure for ~9 hours across 6 attempts. POST /api/partner/webhooks/test delivers a signed ping right now and reports the outcome; GET /api/partner/webhooks/deliveries is the per-endpoint delivery log (every attempt’s status, response code, and error, kept for 60 days), so “did you get it” is answerable from your side.
  • Firm accounts: risk controls, trading halt, and balance operations. The control half of the Firm accounts group. GET/PUT /api/partner/accounts/risk reads and sets the five per-account risk controls (daily/weekly loss, daily/weekly profit, end-of-day auto-close), the same controls the trader sees, enforced live by the risk monitor from the moment of the write. POST /api/partner/accounts/halt blocks all new orders until your firm’s own POST /api/partner/accounts/resume; the halt survives restarts, the trader cannot self-unlock it, and it never replaces a lock that is not yours. POST /api/partner/balance-op credits, debits, or adjusts an account outside of trading, ledger-first on your referenceId, so a retried payout can never fire twice.
  • Firm accounts: provision and reset evaluation accounts by API. A new route group for partner firms running traders on the trdrs venue. POST /api/partner/accounts batch-creates evaluation accounts for your traders by email (per-item results, idempotent on your own referenceId); GET /api/partner/accounts lists the accounts your firm provisioned, with live balances; and POST /api/partner/accounts/reset puts one back to its starting state, ledger-first so a retried reset can never fire twice. Every route is scoped to accounts your firm created through the API: an account the trader opened themselves is invisible here by construction.
  • GET /api/partner/usage/accounts is live. The drill-down behind the usage number: each account that counted in the month, with the real fills that made it count and their first/last timestamps. Computed from the same query as the count, so the list always sums to the billed number: invoice reconciliation without asking us for a breakdown, account by account.
  • GET /api/partner/usage is live. A firm can now pull its own metered usage for a month under its partner key: the distinct accounts that actually traded, the agreed per-account price, and the total. It is the same computation the trdrs invoice is built from, so a bill can be reconciled straight from the API. Scoped to the calling firm by its key, with no parameter that widens it, and read-only.
  • Keys, permissions, and data policy are documented. API Standards gained a Data policy page (what the engine stores, who sees what, and the per-trader market data rule), and the Keys page now covers all three permission levels, issuance, and revocation.
  • Programmatic accounts. Operator Concepts gained a guide to running Connect claim registration, listing, and revocation as an automated pipeline. Its initial handover modes were retired before the first partner integration on 2026-08-29.
  • The Quick Start examples now match the wire exactly. History takes instrument, tf, and countBack; orders take instrument and orderType with broker/account as query parameters. The previous examples used field names the engine never accepted.
  • Worked examples on every operation. All 38 operations now render one coherent trading session instead of placeholder payloads: real ES prices, a fill that reappears in the executions ledger, an evaluation whose drawdown floors follow from its program’s terms.
  • TypeScript and curl samples on every operation. Streaming routes show a real SSE reader, cookie-authenticated routes show first-party fetch, and every sample targets the production host with realistic parameter values.
  • The spec’s first server is production (https://app.trdrs.co), so generated snippets never point at a relative host.

2026-08-23

  • The docs moved to this site. Overview, Quick Start, API Standards, Conformance, Operator Concepts, HTTP clients, and the API Reference now live in one place with one sidebar; old guide URLs redirect.
  • GET /api/market/config declares what the engine accepts: the timeframe grammar with per-unit ceilings, the bars/symbols/quotes limits, and the asset classes this deployment serves. A client can validate an ask without a round trip.
  • The Connect partner routes document the key that actually works there. Partner routes are secured under the partner-scoped key in the spec itself; a tenant key is refused on /api/partner/ by design.

2026-08-22

  • The prop evaluation surface returned as a PREVIEW group. Challenges and enrollments are documented again, marked outside the additive-only guarantee, with the CHALLENGES_ENABLED gate stated on every operation.
  • The reference covers the whole licensed surface, grouped by tag (Market data, Trading, Account, Connect, Challenges), with an onboarding guide.

2026-08-14

  • The holiday calendar is served. GET /api/market/symbol-info carries the session model’s holiday table (sessionCalendar), so clients never ship their own calendar updates.
  • Trailing stops became exclusively engine-managed. The broker-native trailing_stop entry type is refused with a pointer at the ATM path, which manages trails identically on every venue.

2026-08-05

  • Market data became licensed surface. The market routes answer first-party origins and authenticated principals only, and are documented under the tenant key.

Existing account inventory

GET /api/partner/venues/{venueId}/existing-accounts and GET /api/operator/venues/{venueId}/existing-accounts list prior issued accounts without venue bindings. The read requires account-read authority and changes no balances or trading rules. Cursor pagination includes at most 100 accounts per page.