> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trdrs.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> See what changed in the API, newest first, and what each change asks of your integration.

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](/api/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](/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](/api/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](/api/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](/api-reference/firm-accounts-legacy/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](/api-reference/account/list-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](/api-reference/account/list-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](/guides/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.

## 2026-09-21: Hosted Connect Link and venue hedging preview

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](/concepts),
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](/guides/run-firm-accounts) maps each call to its twin, and
[Venue accounts](/guides/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](/guides/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](/guides/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](/guides/firm-analytics).

## 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](/sandbox#set-your-venue-s-brand).

## 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](/providers/test-evidence).

## 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](/api/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](/api/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](/api/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.
