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

# Changelog API (Testnet)

> API and WebSocket spec changes, anchored to testnet releases

<Note>**Testnet runs `testnet-v1.10.9`.** This page tracks OpenAPI and AsyncAPI spec changes as they land on **testnet**, anchored to the testnet release that first shipped each one — a change appears here before it reaches production, which currently carries these entries through `testnet-v1.8.9`. Building against production? See the [mainnet changelog](/changelog).</Note>

<Update label="September 21, 2026" description="testnet-v1.10.9">
  ## testnet-v1.10.9

  **Ticker-based favorite markets and named subaccounts.** [`/v1/api-meta/userPreferences`](/api-reference/userpreferences/get-user-preferences) gains three preference keys. `favoritePerpMarkets` stores favorites as an array of `BASE-QUOTE` perp tickers (e.g. `["BTC-USD"]`) and `favoriteSpotMarkets` as an array of bare spot base symbols (e.g. `["BTC", "ETH", "NVDA"]`), alongside the existing `favoritedMarkets`, which stays an array of numeric market IDs. `subaccountPreferences` stores a per-subaccount display name — a `SubaccountPreferences` object keyed by subaccount number, each entry `{ "name": "…" }` (name max 64 chars, at most 256 entries), and the first `UserPreferenceValue` allowed to be an object rather than a scalar or array. The key set stays closed: PATCHing a key outside the enum returns `400`.

  **Breaking: affiliate `rollingVolume` renamed to `volume30d`.** On [`GET /v1/affiliate/info`](/api-reference/referral/get-affiliate-info), the affiliate's own trailing-30-day notional fill volume field `rollingVolume` is renamed **`volume30d`** — otherwise unchanged (quote quantums, `null`/`0` while the rollup is briefly unavailable). Update any client reading the old name. The separate `rollingVolume` on the `/v1/accountStats` reads is a different field and is **not** affected.
</Update>

<Update label="September 18, 2026" description="testnet-v1.10.7">
  ## testnet-v1.10.7

  **Look up a single trader on the leaderboard.** [`GET /v1/leaderboard`](/api-reference/public/get-the-all-traders-leaderboard) now accepts an `address` to return just that trader's row — its volume, fees, and realized PnL over the window, plus its `rank` on the `sortBy` column wherever it sits on the board (not only within the top-N). Every leaderboard row also carries that `rank` field, computed on the active `sortBy` column in the same query so a row's columns stay mutually consistent. When `address` is set, `limit` is ignored and an address that did not trade in the window returns an empty `entries` list.

  ## testnet-v1.10.6

  **Spot fills — REST and WebSocket.** [`GET /v1/spotFills`](/api-reference/public/get-spot-fills) returns an account's spot fill history, newest-first — the seizure and loan-settle executions of the lending system (spot has no order book, so there is no maker/taker role or fee). Optional `from`/`to` (epoch **microseconds**) bound the window and optional `accountIndex` selects a subaccount. The new [`spotFills`](/api-reference/channels#spotfills) WebSocket channel streams the same executions live, with a snapshot of recent spot fills on subscribe (skip it with `snapshot: false`). No authentication required for the REST read.

  ## testnet-v1.10.5

  **Dedicated `spotPositions` WebSocket channel.** [`spotPositions`](/api-reference/channels#spotpositions) streams a single account's spot collateral holdings on its own channel, so clients can follow collateral without subscribing to the full perp `positions` stream. The snapshot carries the open holdings keyed by spot asset id; each update carries one changed holding with its post-event size, event valuation `price`, and a `reason` (`DEPOSIT`, `WITHDRAWAL`, `SEIZURE`, `RELEASE`, `TRANSFER`, `UNKNOWN`). With this move the `positions` channel is now **perp-only** — spot collateral no longer rides in a discriminated section of `positions`.

  **Breaking: spot position `qty` renamed to `size`.** On the REST spot-position surfaces — [`GET /v1/spotPositions`](/api-reference/public/get-spot-positions) rows and the spot positions on [`GET /v1/account`](/api-reference/public/get-account) — the holding-amount field `qty` is renamed **`size`**, matching the perp position surfaces. Update any client reading the old field name.

  ## testnet-v1.10.4

  **Affiliates: all-time referred volume.** [`GET /v1/affiliate/info`](/api-reference/referral/get-affiliate-info) adds `totalReferredVolumeAllTime` — the combined lifetime notional fill volume of all of the affiliate's current direct (T1) referees, in quote quantums (1e9 = \$1). Same definition as `totalReferredVolume30d` but with no time window. Reads `0` if the underlying rollup is momentarily unavailable.

  ## testnet-v1.10.3

  **Borrow-interest settlement history.** [`GET /v1/interest`](/api-reference/public/get-interest-payments) returns an account's borrow-interest settlements, newest-first — the lending analog of `GET /v1/funding`: where funding charges an open perpetual, interest charges an open loan. Optional `from`/`to` (epoch **microseconds**) bound the window; when omitted, `to` defaults to now and `from` to 30 days before `to`. No authentication required.
</Update>

<Update label="September 16, 2026" description="testnet-v1.10.2">
  ## testnet-v1.10.2

  **Get a fill by trade id.** [`GET /v1/fill/{tradeId}`](/api-reference/public/get-fill-by-id) returns the single fill your account owns for a given `tradeId` (pass `?address=`, optional `accountIndex`) — the point-lookup companion to [`GET /v1/fills`](/api-reference/public/get-fills). No authentication required.

  ## testnet-v1.10.1

  **`closedPnl` now nets the fill fee.** Across [`GET /v1/fills`](/api-reference/public/get-fills) and the fills/`account` WebSocket rows, `closedPnl` is redefined as the cost-basis release (`old borrowedCapital − new borrowedCapital − Δsize × price`) minus the fill's fee. Opening and same-direction add legs now report `−fee` (previously `"0"`).
</Update>

<Update label="September 11, 2026" description="testnet-v1.9.13">
  ## New features

  **Spot collateral positions.** [`GET /v1/spotPositions`](/api-reference/public/get-spot-positions) returns your open spot collateral positions, keyed by spot asset id — each row carrying the asset's balance and its live `oraclePrice` (the USD price of the position's price-source market, `"0"` while a market is briefly unpriced). The same `oraclePrice` field is added to the spot position rows on [`GET /v1/account`](/api-reference/public/get-account). Two new error reasons, `GetSpotPositionsFailed` and `SpotPositionsHandlerUnavailable`, cover a failed read.

  ## Breaking changes

  **Spot collateral fields renamed to `spotAssetId` / `spotAssetDisplayName`.** The spot-asset identifier is unified across REST and WebSocket onto one name. Anywhere a spot-collateral row or transfer previously carried `assetId` / `asset` (and, on spot position rows, `spotMarketId` / `spotMarketDisplayName`; on the withdraw response, `assetDisplayName`), it now carries **`spotAssetId`** and **`spotAssetDisplayName`**. This touches [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) (request, response, and the `SpotAssetWithdraw` EIP-712 payload — the signed field is now `spotAssetId`), [`GET /v1/account`](/api-reference/public/get-account) (`netDepositsByAsset` and spot position rows), and account transfer updates over both REST and the [`accounts` / `accountTransferUpdates`](/api-reference/channels) WebSocket channels. Update any client reading or signing the old field names.
</Update>

<Update label="September 10, 2026" description="testnet-v1.9.11">
  ## New features

  **`price_move` price-alert notifications.** `GET /v1/api-meta/notifications` adds a new `price_move` notification type, fired when a market's mark price moves 5% or 10% off its most recent 24-hour low (an `up` alert) or high (a `down` alert). It reaches every subscriber who favorites the market or holds a position in it; opt out by adding `price_move` to the `disabledAlerts` [user preference](/api-reference/userpreferences/upsert-user-preferences). The new `NotificationPriceMovePayload` reports the move in display decimals — `direction`, `thresholdBps` (500 or 1000), `changePct`, the anchor extreme (`fromPrice`/`fromTs`) and fire-time price (`toPrice`/`toTs`), `elapsedSecs`, and `positionSide` when the recipient holds a position in the market.
</Update>

<Update label="September 4, 2026" description="testnet-v1.9.1">
  ## New features

  **Multi-asset spot collateral.** [`GET /v1/spotAssets`](/api-reference/public/get-spot-collateral-assets) returns the assets accepted as multi-asset collateral for lending — not tradable order books — for example `SPY-USD-SPOT`, each with its LTV, liquidation factor, price-source market, and supply caps. Spot-asset deposits and withdrawals flow through [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) with a new `assetId` selecting the spot asset and a dedicated `SpotAssetWithdraw` EIP-712 payload; they can be rejected with the new statuses `REJECTED_INVALID_MARKET`, `REJECTED_INSUFFICIENT_SPOT_BALANCE`, and `REJECTED_SPOT_POSITION_CAPACITY`. [`GET /v1/account`](/api-reference/public/get-account) gains `netDepositsByAsset`, and [account transfers](/api-reference/public/get-account-transfer-updates) add the types `LENDING_DRAW`, `INTEREST_SETTLEMENT`, `SEIZURE_PAYMENT`, and `CLAIM_REPAYMENT`.

  **Lending rates WebSocket channel.** The global [`lendingRates`](/api-reference/channels#lendingrates) channel streams the exchange-wide borrow rate and cumulative borrow index — the lending analog of `oraclePrices` — emitting one frame per SOFR-driven rate segment with the annual borrow rate the segment opens at (`ratePpm`) and the global `borrowIndex` after the close.
</Update>

<Update label="September 4, 2026" description="testnet-v1.8.12">
  ## Updates

  **New rejection reason `MISSING_MARK_PRICE`.** The market has no live mark price — a transient oracle gap — so the mark-based solvency gate cannot be evaluated. The move is not applied; retry once pricing resumes.
</Update>

<Update label="September 3, 2026" description="testnet-v1.8.9">
  ## New features

  **Invite codes are switched on (testnet only).** [`GET /v1/affiliate/inviteCodes`](/api-reference/referral/list-the-callers-invite-codes) and [`POST /v1/affiliate/redeemInvite`](/api-reference/referral/redeem-an-invite-code) — long present in the spec but answering `503` — now answer on testnet; mainnet still returns `503 {"error":"Invite codes not available yet"}` and the [mainnet changelog](/changelog) will announce that rollout. One invite code is earned per \$1M of lifetime perps volume, and codes materialize lazily — a batch of newly earned codes may take more than one read to appear (`materializationPending`). Codes are bearer secrets that grant perps trading access, so the list read is the one **signed** affiliate read: full header triple, message `X-Timestamp + inviteCodes`. Redemption is single-use and atomic — two callers racing on one code get exactly one win and one `409`.

  The same release settles redemption semantics for callers who are *already referred*: redeeming moves the attribution to the invite code's owner **only when the code is what grants access** — a caller who can already trade and has a referrer gets `409` and the code is not spent. A move is forward-only (the previous affiliate keeps commission already earned and stops accruing) and is confirmed asynchronously, so that path answers `202` rather than `200`. The redemption response also no longer names the code's owner, and [`GET /v1/affiliate/myReferrer`](/api-reference/referral/get-my-referrer) redacts the same address — a referral binding is readable without disclosing the wallet behind it.
</Update>

<Update label="August 31, 2026" description="testnet-v1.8.0">
  ## New features

  **Subaccounts across the WebSocket surface.** Subscriptions to the account-scoped channels — [`account`](/api-reference/channels#account), [`positions`](/api-reference/channels#positions), [`orders`](/api-reference/channels#orders), [`userFills`](/api-reference/channels#userfills), [`funding`](/api-reference/channels#funding), [`accountTransferUpdates`](/api-reference/channels#accounttransferupdates), and [`accountAttributeUpdates`](/api-reference/channels#accountattributeupdates) — take an optional `accountIndex` (0–9). Omit it for subaccount 0, which is what every client written before the field existed keeps getting. It is part of subscription identity: each subaccount is its own subscription with its own snapshot and stream, so one connection may hold several, and `unsubscribe` must repeat the same `accountIndex` — omitting it drops only subaccount 0. Every frame of an account-scoped channel now echoes `accountIndex`, including when it is 0, on `subscribed`, `unsubscribed`, `degraded`, and `channel_data` alike; that is what makes several subaccounts of one address demultiplexable on a single connection, since `id` alone is the master address and identical across all of them. Addresses in `id` come back lowercase whatever spelling was sent, so compare them case-insensitively, and an encoded core account id may be used instead — it already carries its own subaccount index, and supplying `accountIndex` alongside it is allowed only if the two agree. Supplying `accountIndex` on a channel that is not account-scoped is an error rather than an ignored field, as is a value outside 0–9: an out-of-range subaccount can never carry traffic, so accepting one would return a permanently silent subscription.

  ## Updates

  **Per-account sequence numbers on positions and orders.** Position and order rows carry `sequenceNumber` — the per-account sequence of the engine event that last wrote that row, the same counter as `AccountUpdate.sequenceNumber` — so a row can be ordered against account updates without inferring it from arrival order. The [`orders`](/api-reference/channels#orders) snapshot now carries `lastSequenceId` next to `openOrders` and `recentClosedOrders`, matching the [`positions`](/api-reference/channels#positions) snapshot, and streaming `PositionUpdate` envelopes stamp it too, so a client can tell exactly where its snapshot sits in the account's event sequence and discard the updates it already contains. Both are WebSocket-side; REST omits `sequenceNumber` when unset.

  **`marginUsed` reads by margin mode.** On position rows the field now depends on the row's `marginMode`. **CROSS:** the initial margin required at the current mark — |size| × mark × initial-margin fraction — so it moves as the mark moves. **ISOLATED:** the walled-off isolated-leg pool, which does *not* track IMR: it moves only when collateral actually moves, growing one-for-one with [`POST /v1/adjustIsolatedMargin`](/api-reference/exchange/add-or-remove-margin-on-an-isolated-mode-position). The full isolated-leg cash balance is derivable as `marginUsed + borrowedCapital`.
</Update>

<Update label="August 26, 2026" description="testnet-v1.7.10">
  ## Updates

  **New modify rejection reason `MODIFY_SIZE_ALREADY_FILLED`.** Under total-size modify semantics — where the size on a [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) is the order's new *total* size, filled quantity included — a new total at or below the quantity already filled leaves nothing to rest. The original order is canceled (a `CANCELED` update precedes the rejection) and the modify is rejected with this reason. Use [`POST /v1/cancelOrder`](/api-reference/exchange/cancel-order) when the intent is to stop the unfilled remainder.
</Update>

<Update label="August 25, 2026" description="testnet-v1.7.7">
  ## Updates

  **Market filter on the remaining reads and account channels.** An optional `market` — display ticker (`BTC-USD`, case-insensitive) or numeric market id (`1`) — now restricts [`GET /v1/prices`](/api-reference/public/get-live-prices-for-all-markets), [`GET /v1/mids`](/api-reference/public/get-all-mid-prices), [`GET /v1/positions`](/api-reference/public/get-positions), and [`GET /v1/leverages`](/api-reference/public/get-leverage-and-margin-mode-for-an-account-across-all-markets) to a single market; an unresolvable value returns HTTP 400 rather than an unfiltered response. On `prices` and `mids` a filter naming an **offline** market returns an empty object, matching how the unfiltered response omits offline markets; an account with no position in the requested market returns empty `positions`, not a 404. The same filter is accepted on the WebSocket [`positions`](/api-reference/channels#positions), [`orders`](/api-reference/channels#orders), and [`userFills`](/api-reference/channels#userfills) subscriptions, where it applies to the snapshot, the live frames, and the post-snapshot catch-up alike, so the baseline and the diffs always describe the same markets. It is part of subscription identity — one connection may hold several market-scoped views of the same account, and `unsubscribe` must repeat the market it subscribed with. Supplying `market` on any other channel is an error, not an ignored field. Frames on those three channels now carry `market` (the resolved ticker) so a client holding several views can demultiplex, and the `subscribed` / `unsubscribed` acks echo the filter. The WebSocket `get` RPCs take it too, on `fills`, `orders`, `positions`, `leverages`, `mids`, and `prices`.

  **`adjustIsolatedMargin` is confirmed on the `positions` channel.** Adding or removing isolated margin through [`POST /v1/adjustIsolatedMargin`](/api-reference/exchange/add-or-remove-margin-on-an-isolated-mode-position) now emits a `PositionUpdate` on the [`positions`](/api-reference/channels#positions) channel, carrying the originating `requestId` so it can be correlated with the 202 `ACK`, and `marginMode: ISOLATED`. Every position row — snapshot and streaming — now includes `marginMode` (`CROSS` or `ISOLATED`), so the collateral mode of a position is readable without a REST round-trip.

  **`isolated` and `marginMode` are always present on leverage entries.** On the [`accountAttributeUpdates`](/api-reference/channels#accountattributeupdates) channel, `type: "leverage"` entries previously carried the margin mode only when a mode change was part of the same `setLeverage` request. Both fields are now always populated: in snapshot frames they reflect the persisted state, and in streaming frames the account's margin mode after the triggering event, defaulting to `CROSS` if the mode lookup fails.
</Update>

<Update label="August 25, 2026" description="testnet-v1.7.3">
  ## New features

  **Adjust margin on an isolated position.** [`POST /v1/adjustIsolatedMargin`](/api-reference/exchange/add-or-remove-margin-on-an-isolated-mode-position) moves collateral between the cross bucket and an isolated-mode position's leg. `amount` is a dollar decimal string — positive adds margin to the isolated leg, negative removes it, and zero returns HTTP 400; send dollars, not quote quantums. The `(account, market)` must already be in isolated mode with an open position. Like the other engine-confirmed writes it carries a lifecycle `status` separate from the HTTP code: `APPLIED` (200), `ACK` (202, forwarded but unconfirmed — watch the [`positions`](/api-reference/channels#positions) channel), or `REJECTED` (422) with a `rejectReason` of `UNKNOWN_MARKET`, `INVALID_AMOUNT`, `NOT_ISOLATED`, `NO_OPEN_POSITION`, or `UNDERCOLLATERALIZED`. The response echoes `newIsolatedMarginQuoteBalance`, also in dollars.

  **Dead man's switch.** [`POST /v1/scheduleCancel`](/api-reference/exchange/schedule-cancel-all-dead-mans-switch) arms, refreshes, or disarms a per-subaccount deadline; if it passes without a refresh the gateway fires `cancelAllOrders` for that subaccount. Pass an absolute epoch-microsecond `time` to arm or refresh, and omit it (or send null) to disarm. Lead time must be between 5 seconds and 5 minutes, and successful auto-fires are capped at 10 per UTC day per subaccount — past which the endpoint returns HTTP 429. A 503 means the switch is **not** armed (cancel path disabled or store unavailable), so retry rather than assuming coverage.

  ## Updates

  **`netDeposits` semantics.** The field on [`GET /v1/account`](/api-reference/public/get-account) and the [`accounts`](/api-reference/channels#accounts) channel is documented as lifetime net deposits — cumulative inflows minus outflows across every *applied* account transfer, including deposits, withdrawals, internal and self transfers between your own account indices, and referral claims, with rejected transfers excluded. Unlike `equity` and `freeCollateral` it never moves with oracle prices; only a transfer changes it. Subtract it from `equity` for all-time PnL, on the same basis [`GET /v1/portfolio`](/api-reference/public/get-portfolio-history) uses.
</Update>

<Update label="August 25, 2026" description="testnet-v1.7.1">
  ## Updates

  **`status` and `market` filters on order history.** [`GET /v1/orders`](/api-reference/public/get-order-history) and [`GET /v1/openOrders`](/api-reference/public/get-open-orders) accept an optional `status` alongside `market`, which separates orders resting on the book (`OPEN`) from TPSLs parked on the untriggered book (`UNTRIGGERED`). Values are `OPEN`, `UNTRIGGERED`, `FILLED`, `CANCELED`, `REJECTED`, `LIQUIDATED`, and `ADL`, case-insensitive; `status` accepts either a JSON array (`["OPEN","CANCELED"]`) or a comma-separated string (`"OPEN,CANCELED"`), and an unrecognized value returns HTTP 400. It matches an order's *current* status, not any status it held earlier. Both filters apply before the row cap, so a filtered page still carries up to `limit` orders. The same filters are available on the WebSocket `orders` RPC, where they apply to both the `openOrders` and `recentClosedOrders` halves of the result.

  **`from`/`to` on funding are microseconds.** The bounds on [`GET /v1/funding`](/api-reference/public/get-funding-payments) and [`GET /v1/fundingRates`](/api-reference/public/get-market-funding-rates) are epoch **microseconds**, matching the unit of the timestamp field each response reports — so a value read off one page is a valid bound for the next. The spec previously described them as epoch milliseconds; a millisecond value is now far below the accepted minimum.

  **WebSocket error codes completed.** Ten codes the gateway already returned were missing from the published enum: `TimestampReused`, `ModifiesArrayEmpty`, `ModifiesArrayTooLarge`, `RateLimited`, and `AddressNotOnWhitelist` on `post`, and `MarketOffline`, `GetLeveragesFailed`, `GetRateLimitFailed`, `LeverageHandlerUnavailable`, and `RateLimitHandlerUnavailable` on `get`. No behavior change — clients exhaustively matching on the enum can now recognize all of them.
</Update>

<Update label="August 21, 2026" description="testnet-v1.6.0">
  ## New features

  **Batch order modification.** [`POST /v1/batchModifyOrders`](/api-reference/exchange/batch-modify-orders) reprices or resizes up to 100 open orders in one request. Every row must share the same `(address, accountIndex)` — a batch is scoped to a single subaccount — and each row carries its own `signature` over the same typed canonical modify payload (`op` 3) a standalone [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) signs, so the signed bytes are identical either way. Like the other batch routes it authenticates per element rather than with a verified envelope signature, and failures are per-row: a row rejected for validation or signature reasons comes back with `status: ERROR` and an `error` message while the remaining rows still reach the matching engine. The same operation is available over WebSocket.

  **Isolated margin mode.** [`POST /v1/setLeverage`](/api-reference/exchange/set-leverage-for-a-market) accepts an optional `isolated` flag that sets the margin mode for a `(account, market)` alongside the leverage — `true` for isolated, `false` for cross, omitted to leave the mode unchanged. Switching cross → isolated is rejected with `HAS_OPEN_POSITION` when a position is already open in that market; isolated → cross and no-op changes are always accepted. [`GET /v1/leverages`](/api-reference/public/get-leverage-and-margin-mode-for-an-account-across-all-markets) now returns the margin mode (`isolated` / `marginMode`) next to the effective leverage for every market, and the [`accountAttributeUpdates`](/api-reference/channels#accountattributeupdates) channel folds the margin-mode outcome into the same entry via `marginModeStatus` and `marginModeRejectReason`, including on the `leverageReject` entry emitted when the engine refuses a request.

  ## Updates

  **Modify an order by client id.** `ModifyOrderRequest` accepts `clientId` as an alternative to `orderId`, resolved engine-side against the account's client-id index — so an order can be modified before the place response carrying its server-generated id has arrived. Supply exactly one: setting both is rejected, and so is setting neither.

  **`side` filter on order and fill history.** [`GET /v1/orders`](/api-reference/public/get-order-history) and [`GET /v1/fills`](/api-reference/public/get-fills) accept an optional case-insensitive `side` (`BUY` / `SELL`) to restrict results to a single trade direction. Omit it to return both sides; an unknown value returns HTTP 400.

  **Snapshot size control on `userFills`.** Subscribing to the [`userFills`](/api-reference/channels#userfills) channel accepts an optional `nFills` (1–500, default 500) that bounds the initial snapshot. It is ignored on other channels, and live `channel_data` frames are unaffected.
</Update>

<Update label="August 21, 2026" description="testnet-v1.4.8">
  ## Updates

  **Market filter on funding payments.** [`GET /v1/funding`](/api-reference/public/get-funding-payments) accepts an optional `market` to restrict results to a single market, given as either the display name (e.g. `BTC-USD`, case-insensitive) or the numeric market id. Omit it to return every market; an unresolvable value returns HTTP 400.
</Update>

<Update label="August 21, 2026" description="testnet-v1.4.7">
  ## New features

  **Scoped API keys and API-key-signed withdrawals.** [`GET /v1/apiKeys`](/api-reference/onboarding/get-api-keys) entries carry an optional `permissions` array describing scopes granted beyond the default. Every key can read and trade; the `withdraw` scope additionally authorizes [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) using the `X-API-Key` / `X-Timestamp` / `X-Signature` header triple with an Ed25519 signature over the ordersign `WithdrawV1` typed canonical payload (`op` 5), in which case the body `signature` field is ignored. The key must be bound to exactly the requested `(ethereumAddress, accountIndex)`. Replay protection matches the wallet-signed mode: the `X-Timestamp` drift window plus a single-use `nonce` per `(ethereumAddress, accountIndex)`, with HTTP 409 on reuse. The scope is grantable only through the operator's provisioning flow — keys created via the public [`POST /v1/createApiKey`](/api-reference/onboarding/create-api-key) are always trade-only and are rejected with HTTP 403. The original wallet-signed EIP-712 mode is unchanged, and withdraw-to-self remains the only supported destination on both modes.
</Update>

<Update label="August 21, 2026" description="testnet-v1.4.6">
  ## Updates

  **`GET /v1/apiKeys` spans every subaccount by default.** [`GET /v1/apiKeys`](/api-reference/onboarding/get-api-keys) has no default `accountIndex` — omitting it lists the keys of every subaccount, each entry tagged with the `accountIndex` it belongs to, so a listing can be grouped client-side without one request per subaccount. Pass `accountIndex` to scope the listing to a single subaccount. Unlike other account-scoped reads, an omitted value is not the same as `accountIndex=0`, and a non-integer or out-of-range value returns HTTP 400 rather than silently falling back to 0. The response is all-or-nothing across subaccounts: an unreadable subaccount surfaces as HTTP 500 rather than a short list that would look like a revoked key.
</Update>

<Update label="August 21, 2026" description="testnet-v1.4.5">
  ## Updates

  **Market and role filters on order and fill history.** [`GET /v1/orders`](/api-reference/public/get-order-history) and [`GET /v1/fills`](/api-reference/public/get-fills) accept an optional `market`, and [`GET /v1/fills`](/api-reference/public/get-fills) additionally accepts a case-insensitive `role` to restrict results to a single liquidity role. Market filters across the API — including [`GET /v1/markets`](/api-reference/public/get-markets) — now accept either the display name (case-insensitive) or the numeric market id, and an unresolvable value returns HTTP 400.

  **Multiple orderbook aggregations per market on one connection.** A connection may subscribe to the [`l2Orderbook`](/api-reference/channels#l2orderbook) channel more than once for the same market as long as each subscribe uses a distinct (`sigFigs`, `roundStep`) pair. `subscribed`, `channel_data`, and `unsubscribed` frames echo those fields — omitted for full-precision views — so a client holding several views of one market can demux them. Unsubscribing must repeat the same pair to drop one view without affecting the others.
</Update>

<Update label="August 17, 2026" description="testnet-v1.4.3">
  ## New features

  **API-key replay protection & subaccount scoping.** [`POST /v1/createApiKey`](/api-reference/onboarding/create-api-key) and [`POST /v1/revokeApiKey`](/api-reference/onboarding/revoke-api-key) accept an optional `nonce` (single-use per `(address, accountIndex)`) for replay protection and an `accountIndex` (0–9) that scopes the key to a subaccount. Both are bound into the EIP-712 signature: a non-zero `accountIndex` must use the new accountIndex-bearing `CreateApiKey`/`RevokeApiKey` type, and including a `nonce` requires the combined type that binds both. Send the nonce as nanoseconds since the Unix epoch (accepted within −48h/+24h; a legacy opaque UUID also works); a replayed nonce returns HTTP 409. Index 0 still accepts the original index-less type and the legacy EIP-191 fallback during the migration window.

  ## Updates

  **New order-rejection reason `POSITION_SIZE_CAP_EXCEEDED`.** Orders, modifies, or fills that would exceed a market's per-market position notional cap are rejected with `POSITION_SIZE_CAP_EXCEEDED` — distinct from `UNDERCOLLATERALIZED`, since a fully funded account can still hit it.
</Update>

<Update label="August 17, 2026" description="testnet-v1.4.2">
  ## Breaking changes

  **Time-window bounds are now microseconds.** The `from`/`to` query parameters on [`GET /v1/openOrders`](/api-reference/public/get-open-orders), [`GET /v1/orders`](/api-reference/public/get-order-history), [`GET /v1/fills`](/api-reference/public/get-fills), [`GET /v1/trades`](/api-reference/public/get-recent-public-trades), and [`GET /v1/accountTransferUpdates`](/api-reference/public/get-account-transfer-updates) are now epoch **microseconds**, matching the `createdAt` / `updatedAt` / `timestamp` precision these endpoints already report — so a value read from one page is a valid bound for the next with no conversion. Millisecond- or second-scale values are rejected with HTTP 400 (the server requires at least `1e14`); a millisecond bound read as microseconds lands in 1970 and silently returns nothing. Clients that previously sent milliseconds must update.
</Update>

<Update label="August 17, 2026" description="testnet-v1.4.1">
  ## Updates

  **`GET /v1/openOrders` is now bounded and paginated.** [`GET /v1/openOrders`](/api-reference/public/get-open-orders) accepts `limit` (1–1000; default and maximum both 1000, larger values silently clamped) plus `from` / `to` (epoch microseconds) to bound the window on `createdAt`. Responses stay newest-first; page backward by sending the oldest `createdAt` you received as the next request's `to`, deduplicating by `orderId` since the inclusive bound overlaps page edges. An account can hold up to 10,000 live orders, so more than one page may be needed.
</Update>

<Update label="August 3, 2026" description="testnet-v1.3.9">
  ## Breaking changes

  **Deprecated `*Bps` fee-tier aliases removed.** The `makerFeeBps`/`takerFeeBps` fields on [`GET /v1/feetiers`](/api-reference/public/get-fee-tier-table), [`GET /v1/account/stats`](/api-reference/public/get-account-stats), and `feeTierConfig` entries on [`exchangeAttributeUpdates`](/api-reference/channels#exchangeattributeupdates) — along with the spot schedule's `maker_fee_bps`/`taker_fee_bps` — are gone, completing the ppm rename announced in `testnet-v1.2.31`. Use `makerFeePpm`/`takerFeePpm` (and `maker_fee_ppm`/`taker_fee_ppm`); the values are unchanged.
</Update>

<Update label="August 3, 2026" description="testnet-v1.3.8">
  ## New features

  **Orderbook price aggregation.** [`GET /v1/l2OrderBook/{market}`](/api-reference/public/get-l2-orderbook-snapshot) and the [`l2Orderbook`](/api-reference/channels#l2orderbook) channel accept an optional `sigFigs` (2–5) to bucket price levels to that many significant figures, plus `roundStep` (1, 2, or 5) to refine the finest bucket when `sigFigs` is 5. Bids round down and asks round up so the displayed spread is never tighter than the true book, merged sizes are summed, and aggregation is applied before `nLevels` truncation. Omit `sigFigs` for full precision.

  ## Updates

  **Order-size bounds on markets.** [`GET /v1/markets`](/api-reference/public/get-markets) and the [`markets`](/api-reference/channels#markets) channel add `minOrderSize` and `maxOrderSize` (base-asset units). `maxOrderSize` is a per-order security gate rather than a dust filter — reduce-only orders are not exempt.
</Update>

<Update label="August 3, 2026" description="testnet-v1.3.7">
  ## Updates

  **Explicit RTH-transition events on marketAttributes.** The [`marketAttributes`](/api-reference/channels#marketattributes) channel's `boundEvent` adds `exitRth` / `enterRth` for Regular Trading Hours boundary crossings, and is now present on every delta entry — only snapshots omit it. `boundSide` is likewise always present on deltas (`null` on RTH transitions and VWAP seals).

  **Portfolio PnL rebased per timeframe.** `pnlHistory` on [`GET /v1/portfolio`](/api-reference/public/get-portfolio-history) now rebases each timeframe so its first point is `0` — every point is the trading + funding PnL earned since the start of that timeframe, and lifetime PnL is the last point of the `all` timeframe. The curve still does not jump on deposits or withdrawals.
</Update>

<Update label="July 30, 2026" description="testnet-v1.3.6">
  ## New features

  **Off-hours market attributes over WebSocket.** New [`marketAttributes`](/api-reference/channels#marketattributes) channel streams per-market Regular Trading Hours / off-hours trading-band **state** for every online market — `isOutsideRth`, the sealed `currentSettlementPrice` anchor, both current trading-band edges and their next-level edges, and per-side expansion-zone state with its clocks. It is a global channel (no `id`): a full per-market snapshot on subscribe (`isSnapshot: true`), then a single-market update on each RTH boundary crossing, VWAP seal, and off-hours bound event — each carrying that market's complete post-event block plus a `timestamp` and `marketSequenceNum`. Fields that do not apply are `null` (24/7 crypto markets are included with `isOutsideRth: false` and all band fields `null`). Supersedes the `marketAttributes` entry variant on [`exchangeAttributeUpdates`](/api-reference/channels#exchangeattributeupdates).
</Update>

<Update label="July 29, 2026" description="Documentation">
  ## Updates

  **Docs default to mainnet.** The API playground and the curl / Python / JavaScript code samples throughout the [API reference](/api-reference/introduction) now target the production host — `https://api.arcus.xyz` (REST) and `wss://api.arcus.xyz/v1/ws` (WebSocket) — instead of testnet. Testnet (`api.testnet.arcus.xyz`) remains fully available and is still selectable from the server dropdown on every endpoint and channel page; the two environments share identical paths, payloads, and signing, so a sample runs unchanged against either by switching the host. This supersedes the earlier note (below) that samples and the playground targeted testnet.
</Update>

<Update label="July 28, 2026" description="testnet-v1.3.5">
  ## Updates

  **Forced closures are marked on fills.** Fill rows add a `liquidation` object — `method` (`LIQUIDATION` or `ADL`) plus `liquidatedUser` — present on the liquidated account's leg of a liquidation and the deleveraged counterparty's leg of an auto-deleverage; absent on voluntary fills. The marker is persisted, so [`GET /v1/fills`](/api-reference/public/get-fills) and [`userFills`](/api-reference/channels#userfills) snapshots return it too, and it is the supported way to detect forced closures (the `liq:` order-ID prefix is an internal detail). On the liquidated leg, `fee` carries the liquidation penalty paid to the insurance fund and `closedPnl` reads `"0"` by design — the realized loss is already reflected in `netQuoteBalance` on the accompanying account update.
</Update>

<Update label="July 28, 2026" description="testnet-v1.3.3">
  ## New features

  **Account transfer events over WebSocket.** New [`accountTransferUpdates`](/api-reference/channels#accounttransferupdates) channel streams deposits, withdrawals, internal transfers, and referral claims for an account: a newest-first snapshot on subscribe, then one event per transfer as the engine applies it. Amounts are decimal strings in quote currency, and `globalSequenceId` gives strict ordering. The REST equivalent is [`GET /v1/accountTransferUpdates`](/api-reference/public/get-account-transfer-updates).

  **Predicted funding over WebSocket.** New [`predictedFunding`](/api-reference/channels#predictedfunding) channel streams the predicted next-hour funding rate per market as a `{market, rate1h}` payload, refreshed each time the funder recomputes the prediction. Unlike most channels, payloads are not wrapped in an `isSnapshot` envelope.

  ## Updates

  **Subaccount selector on all account-scoped reads.** The optional `accountIndex` query parameter (0–9, default 0) previously accepted only by [`GET /v1/fills`](/api-reference/public/get-fills) now applies across account-scoped endpoints: `GET /v1/account`, `/v1/positions`, `/v1/openOrders`, `/v1/orders`, `/v1/order/{orderId}`, `/v1/funding`, `/v1/accountTransferUpdates`, `/v1/portfolio`, `/v1/rateLimit`, `/v1/leverages`, and `/v1/apiKeys`. Values above 9 return `400`.

  **Faster orderbook snapshots.** The [`l2Orderbook`](/api-reference/channels#l2orderbook) full-snapshot cadence tightened from \~500 ms to \~200 ms.
</Update>

<Update label="July 28, 2026" description="testnet-v1.3.1">
  ## Updates

  **24-hour high/low on markets.** [`GET /v1/markets`](/api-reference/public/get-markets) and the [`markets`](/api-reference/channels#markets) channel add `high24h` and `low24h` — highest and lowest transaction price over the trailing 24-hour window, computed from closed 1-minute candles with at least one trade and omitted when no trades occurred in the window.
</Update>

<Update label="July 28, 2026" description="testnet-v1.2.31">
  ## Updates

  **Fee-tier fields renamed to ppm.** `makerFeeBps`/`takerFeeBps` become `makerFeePpm`/`takerFeePpm` in the fee-tier shape returned by [`GET /v1/feetiers`](/api-reference/public/get-fee-tier-table), [`GET /v1/account/stats`](/api-reference/public/get-account-stats), and `feeTierConfig` entries on [`exchangeAttributeUpdates`](/api-reference/channels#exchangeattributeupdates); the spot fee schedule likewise renames `maker_fee_bps`/`taker_fee_bps` to `maker_fee_ppm`/`taker_fee_ppm`. The values were always parts-per-million despite the old names. The `*Bps` fields remain as deprecated aliases (identical values) for one release cycle — migrate to the `*Ppm` names now.
</Update>

<Update label="July 23, 2026" description="testnet-v1.3.0">
  ## Breaking changes

  **Testnet withdraw/transfer signing domain rotated.** The July 22 testnet reset rotated the `BridgeVault` `verifyingContract` for [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) and [`POST /v1/transfer`](/api-reference/exchange/submit-internal-transfer) to `0x9a6d3499149fea853efe775a3577701539054eaf`; signatures against the previous address are rejected with `401`. This address rotates on every testnet reset — read the current value from the withdraw endpoint's per-environment table. The [funding guide](/guides/fund-testnet-account) contract addresses rotated in the same reset.

  ## Updates

  **Perp candle timestamp bounds declared in the schema.** [`GET /v1/candles`](/api-reference/public/get-ohlcv-candles) `from`/`to` (Unix **microseconds**) now declare `minimum: 1e14`, matching what the handler always enforced.
</Update>

<Update label="July 22, 2026" description="testnet-v1.2.6">
  ## Updates

  **API key create/revoke signing migrates to EIP-712.** [`POST /v1/createApiKey`](/api-reference/onboarding/create-api-key) and [`POST /v1/revokeApiKey`](/api-reference/onboarding/revoke-api-key) are now authenticated by an **EIP-712 typed-data signature** (`eth_signTypedData_v4`) instead of an EIP-191 `personal_sign`. Both schemes are accepted during the migration window; EIP-191 is deprecated and will be rejected once the window closes. Always send `validUntil` explicitly and sign that value — omitting it makes the server verify against its own default. See each endpoint description for the domain, types, and signing examples.

  **Spot candle timestamp bounds declared in the schema.** [`GET /v1/api-meta/candles`](/api-reference/marketmetadata/get-spot-candles) `from`/`to` (Unix **seconds**) declare the `[1, 1e11)` window, rejecting millisecond-scale values.
</Update>

<Update label="July 20, 2026" description="testnet-v1.2.3">
  ## New features

  **Internal transfers between subaccounts.** New [`POST /v1/transfer`](/api-reference/exchange/submit-internal-transfer) moves collateral between two account indexes of the **same wallet** — in v0 there is no cross-wallet recipient. The request is signed with an EIP-712 typed-data signature over the `Transfer` payload (`eth_signTypedData_v4`) and carries a single-use nanosecond-timestamp `nonce` for replay protection (a repeated nonce returns `409`). `amount` is a quote-quantum integer string (1e9 = \$1); unlike withdrawals it never settles on-chain, so it is not constrained to collateral-base-unit multiples. Acceptance is asynchronous: the response carries `status: PENDING` and a `transferId`, with the terminal outcome delivered on the account transfer update stream — insufficient free collateral rejects there with `REJECTED_INSUFFICIENT_COLLATERAL`.

  **Per-market open-interest caps.** [`GET /v1/markets`](/api-reference/public/get-markets) and the `markets` WebSocket channel add `openInterestCap` — the operator-set maximum open interest as a USD-notional decimal string (present only while a cap is active). While a market is at its cap, the engine rejects OI-increasing fills with the new rejection reason `OPEN_INTEREST_CAP_EXCEEDED`; OI-neutral and OI-reducing trades (closes, and opener-vs-closer pairings) still match. Fills executed before the halt stand, and the cap clears as open interest falls or the operator raises it.

  ## Updates

  **Signed withdrawals are single-use.** [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) now rejects a replayed `(ethereumAddress, accountIndex, nonce)` with `409` — resubmitting an identical signed body (or any body reusing the same `nonce`) no longer queues a second withdrawal. Use a fresh nonce for each new withdrawal; the recommended nonce is the current time in nanoseconds since the Unix epoch.
</Update>

<Update label="July 20, 2026" description="testnet-v1.2.2">
  ## Updates

  **API keys are scoped to `(address, publicKey)`.** An Ed25519 public key is globally unique across accounts: [`POST /v1/createApiKey`](/api-reference/onboarding/create-api-key) returns `409 Conflict` when the `publicKey` is already registered to a different account (including expired-but-unrevoked keys), and only the owning account may register or renew it. [`POST /v1/revokeApiKey`](/api-reference/onboarding/revoke-api-key) returns `404` when the key is not registered to the caller's account — including cross-account revoke attempts — with no side effects.
</Update>

<Update label="July 20, 2026" description="testnet-v1.2.1">
  ## New features

  **Sectioned account stats.** [`GET /v1/account/stats`](/api-reference/public/get-account-stats) is now sectioned so clients fetch only what they need via the `include` query parameter: `feeTier` (the current fee tier — cheap, right shape for frequent fee-rate polling) and `volumes` (notional fill volume and fees paid over the 14-day fee-tier rolling window and all-time — backed by analytics storage, fetch on demand). The `volumes` section also accepts opt-in fixed windows via `windows=24h,7d,30d`.

  **Market filter on fills.** [`GET /v1/fills`](/api-reference/public/get-fills) adds an optional `market` query parameter to restrict fill and trade history to a single market, alongside the existing `from`/`to` time bounds and `accountIndex` subaccount selector.
</Update>

<Update label="July 15, 2026" description="testnet-v1.2.0">
  ## Breaking changes

  **Withdraw EIP-712 signing domain rotated; production domain published.** The `BridgeVault` `verifyingContract` for [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) changed on staging (`0xfcb43af23e80dbbe7d951af49aa4eefb4eff8c2c`) and testnet (`0x317253c5c134c3ac60e84c4ca069c9f588030480`) — withdrawal signatures produced against the previous addresses are rejected. The production domain is now published: `chainId` `4663`, `verifyingContract` `0x14b107cf534239c59571b066cb6497a321da897c`.

  ## Updates

  **Modify order is no longer marked "coming soon".** The stale caveat claiming [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) returns `501 Not Implemented` is removed — the endpoint has been live for some time. The docs now also spell out the queue-priority contract: a modify is applied **in place** (keeping queue priority) only when it leaves the price unchanged and reduces size; any price move or size increase is an atomic **cancel + replace** that loses priority. On the `orders` WebSocket channel a non-crossing modify arrives as a single update; a crossing modify arrives as a fill followed by a `PLACED` (or `REJECTED`) — no separate cancel/place event pair is emitted.

  **`goodTilTime` is required on every order — the schema now says so.** The order schema previously required `goodTilTime` only for resting time-in-forces (`GTT`, `ALO`) and described it as "ignored" for `IOC`/`FOK`; the API has always rejected orders without it. It is now documented as unconditionally required on [`POST /v1/placeOrder`](/api-reference/exchange/place-order), the batch variants, and the WebSocket order payloads: for `IOC`/`FOK` it acts as a mandatory replay-protection timestamp, and it must be at least one month in the future.

  **Time-in-force: submit `GTT`, read `GTC`.** Order **submissions** must use `GTT` (Good Till Time) for resting orders, but order **reads** — REST order queries and the `orders` WebSocket channel — currently report resting orders as `GTC`, the legacy name for the same bucket. A new `TimeInForceResponse` schema documents the read-side enum; treat `GTC` and `GTT` as equivalent when reading.

  **`modifyOrder` signing payload documented in full.** The typed canonical payload for `modifyOrder` now shows its complete field set — `{"ad","ai","c","ct","g","id","m","op":3,"p","q","r","s","t","v"}`. `id` is always required, and `g`/`r`/`s`/`t` echo the resting order's `goodTilTime` (in nanoseconds), `reduceOnly`, side, and time-in-force. The modify request body now also requires `reduceOnly` and `goodTilTime` to be echoed explicitly (a differing `goodTilTime` performs a cancel-replace with the new expiry).

  **Subaccount authorization on order writes.** The `403` responses on [`POST /v1/placeOrder`](/api-reference/exchange/place-order), [`POST /v1/cancelOrder`](/api-reference/exchange/cancel-order), the batch variants, and [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) now also cover the case where `accountIndex` does not match the API key's authorized subaccount.
</Update>

<Update label="July 13, 2026" description="testnet-v1.2.0">
  ## Breaking changes

  **Off-hours trading-band fields reshaped.** On [`GET /v1/markets`](/api-reference/public/get-markets) and the `markets` WebSocket channel, the off-hours band is now reported as **actual USD price boundaries** rather than multiplier strings. `currentTradingBound` / `nextTradingBound` are removed; use `upperTradingBound` / `lowerTradingBound` (plus `nextUpperTradingBound` / `nextLowerTradingBound`) and the new per-side expansion-zone state — `isUpperInExpansionZone` / `isLowerInExpansionZone`, `upperZoneEnteredAt` / `lowerZoneEnteredAt`, and `upperExpectedExpansionAt` / `lowerExpectedExpansionAt`. Each side's band now expands independently on the `0.5×` → `1×` → `2×` → `4×` ladder.

  **Rejection reason consolidated.** `ORDER_WILL_TAKE_LIQUIDITY_DURING_MARKET_HALT` and `PRICE_WILL_EXCEED_MAXIMUM_OUTSIDE_RTH_TRADING_BOUND` are **deprecated** (no longer emitted) and replaced by a single `FILL_WILL_EXCEED_TRADING_BOUND`, covering crossing takers, crossing `modifyOrder` cancel-replaces, triggered TPSLs, and placements that would rest reachable beyond the band.

  ## New features

  **Maximum order size enforced.** [`POST /v1/placeOrder`](/api-reference/exchange/place-order), [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order), and the batch variants now reject an order whose `quantity` exceeds the market's `maxOrderSize` (see [`GET /v1/markets`](/api-reference/public/get-markets)) with `OrderSizeTooLarge`. Reduce-only orders are **not** exempt.

  **Market-attribute events over WebSocket.** The `exchangeAttributeUpdates` channel now emits `marketAttributes` entries carrying per-market Regular Trading Hours and off-hours band events: RTH boundary crossings (`isOutsideRth`), VWAP settlement seals (`sealedVwapSettlementPrice`), and expansion-zone / band-expansion events (`boundEvent`: `expansionZoneEntered`, `expansionZoneExited`, `tradingBoundExpanded`).

  ## Updates

  **Candle prices are oracle-derived.** [`GET /v1/candles`](/api-reference/public/get-ohlcv-candles) `open` / `high` / `low` / `close` are derived from the market's oracle price feed, not executed trades. `volume`, `takerBuyVolume`, the notional volumes, and `tradeCount` remain trade-derived statistics for the bucket.
</Update>

<Update label="July 8, 2026" description="testnet-v1.1.98">
  ## Breaking changes

  **`reduceOnly` no longer requires `IOC` or `FOK`.** [`POST /v1/placeOrder`](/api-reference/exchange/place-order) and [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) now accept `reduceOnly: true` with any `timeInForce` — the previous constraint forcing reduce-only orders to be `IOC` or `FOK` is removed. Reduce-only position semantics are enforced regardless of TIF, so a resting reduce-only order is now valid.

  ## New features

  **Rolling fees-paid on account stats.** [`GET /v1/account/stats`](/api-reference/public/get-account-stats) adds `rollingFeesPaid` — perps trading fees paid over the 14-day fee-tier rolling window (quote quantums, 1e9 = \$1), the same window as `rollingVolume`. It's computed from an hourly rollup, so it can trail live fills by up to the current partial hour, and reads `0` if that rollup is temporarily unavailable.
</Update>

<Update label="July 6, 2026" description="Mainnet">
  ## New features

  **Lightweight referral-code lookup.** New [`GET /v1/affiliate/code`](/api-reference/referral/get-the-callers-referral-code) returns just `{ address, code, isAffiliate }` from a single point read — none of the volume, commission, or fee-tier enrichment that `GET /v1/affiliate/info` performs. It's meant for UI surfaces that only need the code (an "Invite Friends" / copy-referral-link control) and may poll every session. Public — no `X-API-Key`, no signature. "Not an affiliate" is a valid empty state served as `200` with `code: ""` and `isAffiliate: false`, never a `404`, so callers don't have to overload the status code. Initially mainnet-only; available on both environments since mid-July 2026.
</Update>

<Update label="July 1, 2026" description="testnet-v1.1.98">
  ## Breaking changes

  **Market `category` values are now upper-case and add `CRYPTO`.** The `category` field on [`GET /v1/api-meta/overview`](/api-reference/marketmetadata/get-market-overview) and [`GET /v1/api-meta/spot/overview`](/api-reference/marketmetadata/get-spot-market-overview) now returns `EQUITIES`, `COMMODITIES`, `INDICES`, or `CRYPTO`, replacing the previous lower-case `stocks` / `commodities` / `indices`. Still omitted for underlyings outside the classified universe.

  ## New features

  **Per-subaccount rate-limit snapshot on write responses.** [`POST /v1/placeOrder`](/api-reference/exchange/place-order), [`POST /v1/batchPlaceOrders`](/api-reference/exchange/place-batch-orders), [`POST /v1/cancelOrder`](/api-reference/exchange/cancel-order), [`POST /v1/batchCancelOrders`](/api-reference/exchange/batch-cancel-orders), [`POST /v1/cancelAllOrders`](/api-reference/exchange/cancel-all-open-orders), and [`POST /v1/modifyOrder`](/api-reference/exchange/modify-order) now return a `rateLimit` object (`pool` — `order` or `cancel` — and `remaining` tokens after the request) on both REST and WebSocket, so clients can track their remaining budget without an extra call. `remaining` can be `0` or negative while the request still succeeds when the account is on the drip throttle; `-1` is a sentinel meaning the per-subaccount layer was not enforced for this request.

  **`clientId` echo on rate-limit rejections.** The `429` body now echoes the request's `clientId` (single-order endpoints) or `clientIds` (batch endpoints, positionally aligned with the submitted array), so a client firing concurrent requests can correlate the rejection to a specific order or batch.

  **New modify rejection reason.** `MODIFY_WOULD_CROSS_OUTSIDE_RTH_TRADING_BOUNDARY` — a non-crossing `modifyOrder` reprice rejected because the new price violates the active off-hours trading band. Unlike `PRICE_WILL_EXCEED_MAXIMUM_OUTSIDE_RTH_TRADING_BOUND` (the crossing cancel-replace path, which removes the original), the original order stays resting on the book unchanged.

  **Rejected `setLeverage` is now observable on WebSocket.** The [`accountAttributes`](/api-reference/channels#accountattributes) channel adds a streaming-only `leverageReject` entry — the `REJECTED` half of the `setLeverage` lifecycle. It carries the `rejectReason` (`UNDERCOLLATERALIZED`, `INVALID_LEVERAGE`, or `UNKNOWN_MARKET`) and the originating `requestId` for correlation; an applied change still arrives as a `leverage` entry.

  ## Updates

  **Deterministic market ordering documented.** [`GET /v1/markets`](/api-reference/public/get-markets) now documents that markets are returned sorted by ascending `marketId`, and the mids map on [`GET /v1/mids`](/api-reference/public/get-all-mid-prices) is ordered by ascending `marketId` and includes only ONLINE markets (OFFLINE markets are omitted).

  **Spot candles stream at every resolution.** The api-meta `candles` WebSocket channel aggregates each resolution (`1m` through `1w`) and a subscription selects one via the `SYMBOL/TIMEFRAME` id (e.g. `AAPL/5m`) — previously only `1m` was documented.
</Update>

<Update label="June 30, 2026" description="testnet-v1.1.96">
  ## Breaking changes

  **Position-tiered margin fields removed.** [`GET /v1/api-meta/markets`](/api-reference/marketmetadata/get-market-metadata) and the [`markets`](/api-reference/channels#markets) WebSocket channel no longer return `transferMarginFraction`, `incrementalInitialMarginFraction`, `incrementalPositionSize`, `baselinePositionSize`, or `maxPositionSize`. Initial margin is no longer scaled by position-size brackets in the published market schema; `initialMarginFraction`, `maintenanceMarginFraction`, and the off-hours variants remain.

  **`minOrderSize` and `maxOrderSize` removed.** The same market reads drop `minOrderSize` and `maxOrderSize`, and `minOrderSize` is no longer a required field. Per-market order sizing is now expressed through `minOrderNotional` (the \$5 minimum-notional floor introduced in testnet-v1.1.93) and `stepSize`.
</Update>

<Update label="June 29, 2026" description="testnet-v1.1.93">
  ## Breaking changes

  **All-traders leaderboard replaces the affiliate volume leaderboard.** `GET /v1/affiliate/volumeleaderboard` (`getVolumeLeaderboard`) is gone, replaced by [`GET /v1/leaderboard`](/api-reference/public/get-the-all-traders-leaderboard) (`getTraderLeaderboard`). A new `sortBy` query param (`volume`, `pnl`, or `fees`; default `volume`) chooses the ranking column, and every row now carries `volume`, `feesPaid`, and `pnl` (realized only) together, computed in one query so the three columns are always mutually consistent. The `window` (`all`/`30d`/`24h`, default `30d`) and `limit` (max 100, no cursor) semantics are unchanged.

  **Notification timestamps are now decimal strings.** On `GET /v1/api-meta/notifications`, `createdAtNs` is now a string — the value exceeds 2^53 and a JSON number would lose precision in JS clients. Pass it back verbatim as the `before_ns` cursor (also now a decimal-string query param) and inside `ids[].created_at_ns` on `:markSeen`; a truncated value targets the wrong row. `:markSeen` still accepts a bare number for backward compatibility.

  **Minimum order notional enforced.** [`POST /v1/placeOrder`](/api-reference/exchange/place-order) and [`POST /v1/batchPlaceOrders`](/api-reference/exchange/place-batch-orders) now reject position-opening orders whose notional (`quantity` × `price`) is below **\$5** with `InvalidRequest`. Reduce-only orders (including TPSLs) are exempt. The per-market floor is published as `minOrderNotional` on [`GET /v1/api-meta/markets`](/api-reference/marketmetadata/get-market-metadata).

  **API key `keyName` removed.** [`POST /v1/createApiKey`](/api-reference/onboarding/create-api-key) no longer accepts or returns the optional `keyName` label.

  ## New features

  **Deposit & withdrawal notifications.** The notification inbox now surfaces `deposit` and `withdrawal` events through a new `NotificationTransferPayload` (`subaccountIdx`, `amount`, `netQuoteBalance`, `operationId`). These events are not market-scoped, so the enclosing `marketId` is `0`.

  **Off-hours trading-band fields.** [`GET /v1/api-meta/markets`](/api-reference/marketmetadata/get-market-metadata) and the [`markets`](/api-reference/channels#markets) WebSocket channel now expose `currentSettlementPrice`, `currentTradingBound`, and `nextTradingBound` for equity-class markets while outside regular trading hours (omitted in-RTH and for 24/7 crypto markets).

  **Lifetime volume & fees on the fee-tier read.** [`GET /v1/feetiers`](/api-reference/public/get-fee-tier-table) now returns `lifetimeVolume` and `lifetimeFeesPaid` alongside the existing 14-day `rollingVolume`.

  ## Updates

  **Open interest is now populated.** `openInterest` on [`GET /v1/api-meta/markets`](/api-reference/marketmetadata/get-market-metadata) and the `markets` WebSocket channel now reports the total size of open long positions (equal to total short, in base-asset units), refreshed from a periodic snapshot, instead of the previous placeholder `"0"`.

  **Market category on overview reads.** [`GET /v1/api-meta/overview`](/api-reference/marketmetadata/get-market-overview) and [`GET /v1/api-meta/spot/overview`](/api-reference/marketmetadata/get-spot-market-overview) add a `category` field (`stocks`, `commodities`, or `indices`) for classified equity-class underlyings.
</Update>

<Update label="June 25, 2026" description="Mainnet live">
  ## New features

  **Mainnet is live.** The production API is now serving at `https://api.arcus.xyz` (REST) and `wss://api.arcus.xyz/v1/ws` (WebSocket), selectable from the server dropdown on every endpoint page. Testnet (`api.testnet.arcus.xyz`) remains available, and the two environments share identical paths, payloads, and signing — only the host differs. The code samples and API playground throughout the docs continue to target testnet. See the [API introduction](/api-reference/introduction).
</Update>

<Update label="June 25, 2026" description="testnet-v1.1.91">
  ## Updates

  **Minimum withdrawal size enforced.** [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) now rejects amounts below the collateral currency's minimum with `400` — e.g. `validation error on field 'amount': must be at least 1000000000 quote quantums` (1e9 quote quantums = \$1). Amounts must also be exactly representable in collateral base units.

  **`compliance` address section now conditional.** [`GET /v1/compliance`](/api-reference/public/get-compliance-status) returns the `address` screening section only when the `?address=` query param is supplied; `geo` is always present. `address` is no longer a required response field.

  **Spot price & candle WebSocket payloads documented.** The `api-meta` `prices` and `candles` WebSocket channels now have published payload schemas (`SpotPrices` / `SpotPrice` and `SpotCandlesSubscribed` / `SpotCandlesChannelData`) covering both the subscribe snapshot and the per-tick `channel_data` frames.
</Update>

<Update label="June 24, 2026" description="testnet-v1.1.90">
  ## Breaking changes

  **Order time-in-force rules tightened.** New per-order-type `timeInForce` constraints, enforced at request validation:

  * **Every order must include `goodTilTime`** (epoch microseconds) set **at least one month in the future** — now required even for `IOC` orders, which previously ignored it. A missing value is rejected with `validation error on field 'goodTilTime': is required`.
  * **`MARKET` orders must use `IOC`.** `GTT`, `FOK`, and `ALO` are rejected with `validation error on field 'timeInForce': must be IOC for MARKET orders`.
  * **TPSL orders:** `LIMIT` TPSL legs must use `GTT`; `MARKET` TPSL legs must use `IOC`. (TPSL legs must also be `reduceOnly`, and are placed via `batchPlaceOrders` groupings.)
</Update>

<Update label="June 24, 2026" description="testnet-v1.1.89">
  ## New features

  **Server time.** [`GET /v1/time`](/api-reference/public/get-current-server-time) returns the current server time — useful for aligning the nanosecond signing timestamp with the gateway's ±30s drift window.
</Update>

<Update label="June 22, 2026" description="testnet-v1.1.82">
  ## Breaking changes

  **Order signing moved to the ordersign typed canonical payload.** `placeOrder`, `cancelOrder`, and `modifyOrder` — and each element of `batchPlaceOrders` / `batchCancelOrders` — now sign a compact, key-sorted JSON payload built from the parsed request: the payload object **is** the Ed25519 message, with no `timestamp + action` prefix. The timestamp is carried as the `ct` field (it must equal `X-Timestamp`), and price/size are signed as integer ticks/quantums derived from the market's `tickSize` / `stepSize`. The previous `X-Timestamp + action + canonicalJSON(body)` scheme is now rejected with `401` for these endpoints. `cancelAllOrders`, `setLeverage`, and WebSocket authentication keep the legacy message. See [Authentication](/api-reference/authentication).

  **`GTC` replaced by `GTT`.** The `timeInForce` enum is now `GTT`, `IOC`, `FOK`, `ALO`. Resting orders (`GTT`, `ALO`) require `goodTilTime` (epoch microseconds) set **at least one month in the future** at the time the order is processed; nearer or missing values are rejected.

  **Withdrawals now use EIP-712 typed data.** [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) authenticates with an `eth_signTypedData_v4` signature over a `Withdraw` typed payload (domain and types in the endpoint reference) instead of the previous SHA-256 ECDSA canonical message.

  **Market orders require a protective price bound.** A `MARKET` order's `price` must be within **10%** of the current mark price (TPSL market orders: within 10% of the trigger price), or the order is rejected. With this bound, market orders behave like marketable limit orders.

  ## Updates

  **New order rejection reason.** `PRICE_WILL_EXCEED_MAXIMUM_OUTSIDE_RTH_TRADING_BOUND` is added to the `RejectionReason` set delivered on the `orders` WebSocket channel — emitted when a resting order's price would breach the maximum off-hours trading bound once the current regular-trading-hours session ends.
</Update>

<Update label="June 19, 2026" description="Endpoint migration">
  ## Breaking changes

  **API base URLs moved to `api.testnet.arcus.xyz`.** REST is now `https://api.testnet.arcus.xyz` and WebSocket `wss://api.testnet.arcus.xyz/v1/ws/`. The previous hostnames are being retired — update any saved base URLs, SDK configs, and clients at your earliest convenience to avoid disruption. Paths and payloads are unchanged. See the [API introduction](/api-reference/introduction).
</Update>

<Update label="June 19, 2026" description="testnet-v1.1.79">
  ## New features

  **Spot market overview REST endpoint.** [`GET /v1/api-meta/spot/overview`](/api-reference/marketmetadata/get-spot-market-overview) returns the curated spot market universe in source order, with each entry augmented by `Market Cap` and `logo_url` from the latest stored market metadata profile when available. Public — no `X-API-Key` or signature required.
</Update>

<Update label="June 17, 2026" description="testnet-v1.1.78">
  ## Breaking changes

  **Candles pagination replaced.** [`GET /v1/candles`](/api-reference/public/get-ohlcv-candles) no longer accepts `limit`. A request must now pass `to` — the upper bound of the window as a Unix **microsecond** timestamp (seconds- or milliseconds-scale values are rejected) — and may scope the lower bound with either `countback` (number of closed bars counting back from `to`) or `from` (a microsecond lower bound); `from` and `countback` are mutually exclusive. A request without `to` now returns `400`.

  **`volume30d` renamed to `rollingVolume`.** [`GET /v1/account/stats`](/api-reference/public/get-account-stats) and the affiliate commission and claim schemas now report notional fill volume under `rollingVolume`, measured over the fee-tier rolling window rather than a fixed trailing 30 days. Update any client that reads `volume30d`.

  ## Updates

  **New order rejection reasons.** The `RejectionReason` set delivered on the `orders` WebSocket channel adds `OPEN_ORDER_CAP_EXCEEDED`, `ORDER_NOT_FOUND`, `ORDER_NOT_FOUND_FOR_MODIFY`, `MODIFY_CHANGED_IMMUTABLE_FIELD`, `MODIFY_ZERO_SIZE`, and `ORDER_WILL_TAKE_LIQUIDITY_DURING_MARKET_HALT`.

  **Market asset name in metadata.** Market metadata responses now include `fullAssetName` (e.g. `Bitcoin`) when a canonical name is configured for the market.
</Update>

<Update label="June 16, 2026" description="testnet-v1.1.75">
  ## Breaking changes

  **Unified request signing.** REST and WebSocket now share one signing message: `X-Timestamp + action + canonicalJSON(body)`, where `action` is the canonical operation name — the REST path's final segment or the WebSocket request `type` (e.g. `placeOrder`, `cancelOrder`). The HTTP method and the `/ws/v1/` path prefix are no longer part of the signed message; re-sign with the new message before sending or the request is rejected with `401 invalid signature`. Batch methods sign each element over its singular action (e.g. `placeOrder` for each order in `batchPlaceOrders`) with one shared timestamp. The cutover also invalidates API keys registered before it — re-register via `POST /v1/createApiKey` (EIP-191 registration is unchanged). See [Authentication](/api-reference/authentication).
</Update>

<Update label="June 11, 2026" description="testnet-v1.1.74">
  ## Breaking changes

  **Protected requests now require nanosecond timestamps.** The signing timestamp — `X-Timestamp` for REST, `timestamp` for WebSocket order methods — must now be Unix time in **nanoseconds** as a decimal string (e.g. `1713825891591000000`), not milliseconds. Millisecond or second epochs are rejected with `401 Unauthorized`. Update any client that builds the signing message before re-signing. See [Authentication](/api-reference/authentication).

  ## Updates

  **User-facing timestamps are now microseconds.** Response time fields — candle `openTime`, funding payment times, and order placement/update times — are emitted in Unix microseconds for consistent precision across the API.
</Update>

<Update label="June 11, 2026" description="testnet-v1.1.70">
  ## New features

  **Market overview REST endpoint.** [`GET /v1/api-meta/overview`](/api-reference/marketmetadata/get-market-overview) returns a consolidated per-market overview keyed by base asset, so clients can fetch headline market data in a single call.

  **Volume leaderboard REST endpoint.** [`GET /v1/affiliate/volumeleaderboard`](/api-reference/public/get-the-all-traders-leaderboard) returns the top addresses ranked by signed-notional fill volume over a rolling window — `all`, `30d`, or `24h` (default `30d`).

  **Referral commission rate schedule.** [`GET /v1/commissionrates`](/api-reference/public/get-referral-commission-rate-schedule) returns the referral commission rate schedule used to compute affiliate payouts on each fill.
</Update>

<Update label="June 8, 2026" description="testnet-v1.1.69">
  ## New features

  **Push notification device tokens.** Register an Expo push token for the calling wallet with `POST /v1/api-meta/notifications/tokens` so the notifications consumer can deliver a push when a matching event (resting fill, liquidation, TP/SL trigger) lands, and remove it with `DELETE /v1/api-meta/notifications/tokens`.
</Update>

<Update label="June 5, 2026" description="testnet-v1.1.65">
  ## New features

  **Rate-limit usage REST endpoint.** [`GET /v1/rateLimit`](/api-reference/public/get-current-rate-limit-usage) returns the state of both per-subaccount throttle pools (order and cancel), so clients can self-pace and see which limit is currently constraining them.

  **Withdrawals REST endpoint.** [`POST /v1/withdraw`](/api-reference/exchange/submit-withdrawal) submits a withdrawal of collateral to the chain.

  **Market metadata REST endpoints.** [`GET /v1/api-meta/markets`](/api-reference/marketmetadata/get-market-metadata) returns curated reference data — company profile, branding, headline financials, and TradingView chart identifiers — for one or all markets. Branding icons and market capitalizations are served by [`GET /v1/api-meta/overview`](/api-reference/marketmetadata/get-market-overview), which folds in what were briefly separate `icons` / `marketCaps` projections.

  **Notifications REST endpoints.** List the most recent notifications for an address (newest-first) with `GET /v1/api-meta/notifications`, and mark them read with `PATCH /v1/api-meta/notifications:markSeen` — either an explicit list of `(created_at_ns, notification_id)` pairs, or the most recent `limit` rows for the wallet via `all=true`.

  **Referral code availability.** [`GET /v1/affiliate/codeAvailable`](/api-reference/referral/check-whether-a-referral-code-is-available) returns `{ code, available }` for a single referral code string.
</Update>

<Update label="May 27, 2026" description="testnet-v1.1.56">
  ## New features

  **Fee tier table REST endpoint.** [`GET /v1/feetiers`](/api-reference/public/get-fee-tier-table) returns the full fee tier table, sorted ascending by level (`0=Base` through `4=Platinum`) — the maker/taker fee BPS for each tier and the 30-day notional volume threshold required to reach it.

  **Exchange attribute updates WebSocket channel.** Subscribe to [`exchangeAttributeUpdates`](/api-reference/channels#exchangeattributeupdates) for exchange-wide state, discriminated by entry `type`. The current type, `feeTierConfig`, sends the full fee tier table on subscribe and a fresh replacement whenever an operator updates it — the streaming counterpart to `GET /v1/feetiers`.
</Update>

<Update label="May 27, 2026" description="testnet-v1.1.54">
  ## New features

  **Account attribute updates WebSocket channel.** Subscribe to [`accountAttributeUpdates`](/api-reference/channels#accountattributeupdates) for live per-account state — effective leverage per market and the account's current trading fee tier — discriminated by entry `type` (`"leverage"` or `"feeTier"`). On subscribe the server sends one leverage entry per market plus, when available, one fee tier entry; updates stream a single-entry delta for whichever attribute changed.

  **Leverages REST endpoint.** [`GET /v1/leverages`](/api-reference/public/get-leverage-and-margin-mode-for-an-account-across-all-markets) returns the effective leverage for `(address, accountIndex)` across every market, sorted by ascending `marketId`. Each entry reflects the user's override (set via [`POST /v1/setLeverage`](/api-reference/exchange/set-leverage-for-a-market)) when one exists, otherwise the market default.

  **Account stats REST endpoint.** [`GET /v1/account/stats`](/api-reference/public/get-account-stats) returns 30-day rolling notional fill volume and the current trading fee tier (level + BPS) for any Ethereum address.

  ## Updates

  **Affiliate endpoints moved.** All `/v1/api/affiliate/*` paths are now under [`/v1/affiliate/*`](/api-reference/referral/get-affiliate-info) — the legacy `/api/` segment has been dropped. Update any clients that hard-coded the old prefix. Operation IDs and request/response schemas are unchanged.
</Update>

<Update label="May 19, 2026" description="testnet-v1.1.47">
  ## New features

  **Trades REST endpoints.** Query recent public trades for a market with [`GET /v1/trades`](/api-reference/public/get-recent-public-trades), or fetch a single trade by ID with [`GET /v1/trade/{tradeId}`](/api-reference/public/get-a-single-trade-by-id).

  **Funding payments — REST and WebSocket.** Track per-account funding history with [`GET /v1/funding`](/api-reference/public/get-funding-payments) and per-market rates with [`GET /v1/fundingRates`](/api-reference/public/get-market-funding-rates). Stream live funding payments over the new [`funding`](/api-reference/channels#funding) WebSocket channel — snapshot of the 100 most recent payments on subscribe, then one event per `(market, account)` at each funding time. Positive `payment` = received, negative = paid.

  **Portfolio history.** [`GET /v1/portfolio`](/api-reference/public/get-portfolio-history) returns account equity, PnL, and value-over-time history for charting.

  **Positions REST endpoint.** [`GET /v1/positions`](/api-reference/public/get-positions) returns the current open positions for an account — the REST counterpart to the existing `positions` WebSocket channel.

  **Cancel-all and set-leverage.** [`POST /v1/cancelAllOrders`](/api-reference/exchange/cancel-all-open-orders) cancels every open order for an account in one call; [`POST /v1/setLeverage`](/api-reference/exchange/set-leverage-for-a-market) updates per-market leverage.

  **Account transfer updates.** [`GET /v1/accountTransferUpdates`](/api-reference/public/get-account-transfer-updates) returns deposit/withdrawal history with cursor pagination.

  **User preferences.** Persist client-side UI settings server-side via [`/v1/api-meta/userPreferences`](/api-reference/userpreferences/get-user-preferences).

  **Referral program.** Twelve new endpoints under [`/v1/api/affiliate/*`](/api-reference/referral/get-affiliate-info) cover the full affiliate lifecycle — create and revoke referral codes, register affiliates, view referees and leaderboard, track commissions and kickbacks, and claim rewards.
</Update>

<Update label="May 11, 2026" description="Initial testnet release">
  ## New features

  **REST and WebSocket API now live on testnet.** Trade, stream market data, and manage accounts against `https://api.testnet.arcus.xyz` (REST) and `wss://api.testnet.arcus.xyz/v1/ws/` (WebSocket). Both APIs are non-custodial — register an Ed25519 API key against your Ethereum address and the server never sees a private key. See the [API introduction](/api-reference/introduction) to get started.

  **Real-time market data channels.** Subscribe to live updates over WebSocket:

  * [`l2Orderbook`](/api-reference/channels#l2orderbook) and [`l2OrderbookUpdates`](/api-reference/channels#l2orderbookupdates) — full snapshots every \~500 ms, or initial snapshot plus incremental updates (1–100 levels).
  * [`bbo`](/api-reference/channels#bbo) — best bid and offer at the top of book.
  * [`trades`](/api-reference/channels#trades) — live public trade stream.
  * [`markets`](/api-reference/channels#markets) — global market metadata with funding rate, oracle and index prices, and 24h volume.
  * [`oraclePrices`](/api-reference/channels#oracleprices) — per-market oracle prices from the on-chain Slinky aggregator.
  * [`candles`](/api-reference/channels#candles) — OHLCV across 13 timeframes from `1m` to `1w`, with up to 200-candle snapshots.

  **Account and order channels.** Track your account state and order lifecycle in real time with [`account`](/api-reference/channels#account), [`positions`](/api-reference/channels#positions), [`orders`](/api-reference/channels#orders), and [`userFills`](/api-reference/channels#userfills).

  **Order routing over WebSocket.** Place, cancel, modify, and batch orders directly over WebSocket. All order methods are asynchronous and return a `202 ACK` — observe lifecycle through the `orders` and `userFills` channels. See [Placing orders](/api-reference/websocket#placing-orders).

  **Ed25519 API keys with Ethereum-signed registration.** Generate an Ed25519 key pair, register it with an ECDSA signature from your master Ethereum address, and sign protected requests with a Unix-millisecond timestamp. Full instructions in [Authentication](/api-reference/authentication).

  ## Updates

  **Rebranded to Arcus.** The product is now Arcus across all user-facing surfaces, including the API spec and documentation.

  ## Known limitations

  * `batchModifyOrders` is not yet implemented and returns `501`.
  * `createApiKey` is REST-only; the WebSocket equivalent returns `501 NotImplemented`.
</Update>
