> For the complete documentation index, see [llms.txt](https://docs.reya.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.reya.xyz/developers/devnet/changelog/v2-migration.md).

# V2 perpOB Migration Reference

This page is a **complete inventory** of every change to the Reya V2 integration surface between what runs on **mainnet today** (spot-only V2) and what runs on **devnet1** (the unified perp + spot order book, "perpOB"). It exists so an integrator porting an existing spot or perp-AMM client can migrate in one pass without diffing the specs themselves. It covers the REST + WebSocket V2 endpoints, the shared type system, and on-chain signing.

{% hint style="info" %}
**This is a state-to-state migration reference, not a chronological changelog.**

* **FROM** — mainnet V2 API spec [`2.3.5`](https://github.com/Reya-Labs/reya-api-specs/tree/2.3.5) (spot-only; AMM perps).
* **TO** — devnet1 API spec **3.5.2**, verified September 14, 2026, from immutable [commit `4fafae7`](https://github.com/Reya-Labs/reya-api-specs/tree/4fafae7d19076e8d31811930000a78d842758d9f). Fetch [`GET /v2/openapi-spec.yaml`](https://api-devnet.reya-cronos.network/v2/openapi-spec.yaml) for the contract currently deployed. The SL/TP, risk and depth sections below reflect that live behaviour.

Use this guide for the perpOB migration and the [Changelog](/developers/devnet/changelog/changelog.md) for dated release notes.
{% endhint %}

## How to read this page

Every item is tagged with a **surface** and a **type**, following the [Changelog legend](/developers/devnet/changelog/changelog.md):

* Surface — `[On-chain]` · `[REST]` · `[WS]`. The REST V2 endpoints and the V2 WebSocket surfaces use matching request and response schemas, so most schema changes surface **identically on both**; a row is tagged `[REST]·[WS]` when it applies to both, or with a single surface when it's specific to one.
* Type — **Added** · **Changed** · **Deprecated** · **Removed** · **Behavioural** (same endpoint/field, new runtime behaviour).
* **🔴 BREAKING** — you must change code or config to keep working: a removed/renamed field or endpoint, a changed type/default/semantic, a new required field, or tightened validation. Additive, backward-compatible changes are **not** breaking.

**Breaking changes are marked 🔴 below, alongside additive / non-breaking changes.** The single biggest area is signing & settlement — start there.

***

## 1. Signing & Settlement (`[On-chain]`)

The order-entry EIP-712 contract was rewritten end-to-end. **This is the shared order-entry surface: spot integrators must re-sign too, not just perp integrators.** A client that does not rebuild its signer produces signatures that fail `UNAUTHORIZED_SIGNATURE_ERROR` on *every* order. The canonical field-by-field reference is [Signatures & Nonces](/developers/devnet/authentication/signatures-and-nonces.md).

### 🔴 BREAKING — Order-entry EIP-712 message struct replaced (flat `Order` / `OrderDetails`)

|                  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Old (2.3.5)**  | Sign `ConditionalOrder(uint256 verifyingChainId, uint256 deadline, ConditionalOrderDetails order)`, where `ConditionalOrderDetails` carries 8 fields and packs **quantity + price into an ABI-encoded `inputs` bytes blob** (`abiEncode(['int256','uint256'], [signedQty_e18, price_e18])`) plus an empty `counterpartyAccountIds` array. `orderType` byte for a spot order = `LIMIT_ORDER_SPOT = 6`.                                                                                         |
| **New (3.0.13)** | Sign `Order(uint256 verifyingChainId, uint256 deadline, OrderDetails order)`, where `OrderDetails` is **14 flat fields**: `accountId(uint128)`, `marketId(uint128)`, `exchangeId(uint128)`, `orderType(uint8)`, `quantity(int256)`, `limitPrice(uint256 E18)`, `triggerPrice(uint256 E18)`, `timeInForce(uint8)`, `clientOrderId(uint64)`, `reduceOnly(bool)`, `postOnly(bool)`, `expiresAfter(uint256)`, `signer(address)`, `nonce(uint256)`. No `inputs` blob, no `counterpartyAccountIds`. |

**What to do:** rebuild the order signer from scratch. Emit `quantity` (int256, `+` buy / `−` sell), `limitPrice`, `triggerPrice` (E18, `0` for LIMIT) as direct struct members; drop `inputs` and `counterpartyAccountIds`. The typehash string changes completely, so a structurally-equivalent re-encode of the old fields is still rejected. The same `Order` / `OrderDetails` envelope is reused by `modifyOrder`. Note the signed `OrderDetails` members `limitPrice` / `triggerPrice` are sent on the wire as the JSON request fields `limitPx` / `triggerPx` — same values, wire-name only.

### 🔴 BREAKING — `orderType` enum re-numbered and re-ordered

`OrderType { Limit=0, StopLoss=1, TakeProfit=2 }`. A LIMIT order now signs byte **`0`** (was `6` for `LIMIT_ORDER_SPOT`). This also **inverts** the legacy enum order (old `StopLoss=0`/`TakeProfit=1`). REST string mapping: `LIMIT→0`, `STOP_LOSS→1`, `TAKE_PROFIT→2`. **What to do:** change the signed limit-order byte from `6` to `0`; fix any code that imported the old `StopLoss=0`/`TakeProfit=1` ordering.

### 🔴 BREAKING — `postOnly` is now a signed field (and enforced)

`bool postOnly` is the 11th `OrderDetails` member (between `reduceOnly` and `expiresAfter`), and must be included in every order signature. Its mere presence changes the typehash. **What to do:** add `postOnly` to your encoding **even if you never use it** — sign `postOnly=false` to match today's default. (Enforcement behaviour is covered in §3.)

### 🔴 BREAKING — Two-field time model: `deadline` (signature validity) split from `expiresAfter` (order lifetime)

|                  |                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Old (2.3.5)**  | A single time value. The SDK set the envelope `deadline` and sent it to the API as `CreateOrderRequest.expiresAfter`. There was no separate request `deadline` field.                                                                                                                                                                                                                                                        |
| **New (3.0.13)** | `deadline` (Unix seconds) = EIP-712 **signature-validity** window, signed into the `Order` envelope, **checked at entry, now a required request field**. `expiresAfter` (Unix seconds) = the **GTT order lifetime**, signed into `OrderDetails`; the matching engine auto-cancels expired GTT orders and settlement rejects any fill that lands on-chain after expiry. It is omitted from JSON for GTC/IOC/no-expiry orders. |

**What to do:** compute and sign **both** values. `deadline` short (e.g. `now + 30s`). Omit `expiresAfter` from REST/WS JSON for GTC/IOC/no-expiry orders (the signer encodes the no-expiry value internally); send a future timestamp for GTT (with `deadline < expiresAfter`, leaving margin — a GTT fill mined after `expiresAfter` reverts `OrderExpired`). See §3 for the validation coupling.

### Note — point your `verifyingContract` at the devnet1 `OrdersGateway`

Not a perpOB change — just the usual per-environment address difference. Your signing domain's `verifyingContract` is the **OrdersGateway** proxy for the environment you're on; use the **devnet1** address (see [Contract Addresses](/developers/devnet/reference/contract-addresses.md)), not the mainnet one. The EIP-712 signing recipe itself (domain, message struct, integer widths) is documented in full on [Signatures & Nonces](/developers/devnet/authentication/signatures-and-nonces.md).

***

## 2. REST + WebSocket V2 endpoints (`[REST]` · `[WS]`)

The V2 REST endpoints and the V2 WebSocket surfaces use matching request and response schemas, so most of the changes below surface **identically on both** — a REST response field and the matching WebSocket message field change together. Rows are tagged with the surface they apply to. (Order submission also works over the WebSocket order-entry API with the same payloads, but most integrators use REST.)

### Endpoints

| Change                                                                               | Type    | Old → New                                                                                                                        | What to do                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🔴 **`GET /marketDefinitions` removed** `[REST]`                                     | Removed | AMM-era unprefixed route → canonical `GET /perpMarketDefinitions`                                                                | Replace the URL / generated SDK method with `perpMarketDefinitions`. The response shape is still `MarketDefinition`; only the route is canonicalized for perpOB.                                                                                                                                                                                                                                                                                                                                                                                                                      |
| 🔴 **`GET /liquidityParameters` removed** `[REST]`                                   | Removed | AMM-specific `LiquidityParameters { symbol, depth, velocityMultiplier }` → removed                                               | Delete `LiquidityParameters` consumers. Use `GET /market/{symbol}/depth` for live order-book depth and `GET /perpMarketDefinitions` for static market parameters.                                                                                                                                                                                                                                                                                                                                                                                                                     |
| 🔴 **Execution-bust endpoints + channels renamed** `[REST]·[WS]`                     | Removed | `spotExecutionBusts` → `executionBusts` (REST routes + WS channels)                                                              | Update client URLs / resubscribe; now covers spot **and** perp busts. Schema also renamed (see Execution schemas).                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **Asset oracle price feed added** `[REST]·[WS]`                                      | Added   | n/a → `GET /assetOraclePrices` and WS `/v2/assetOraclePrices`                                                                    | Optional replacement feed for asset Stork oracle prices. Returns `AssetOraclePrice { asset, oraclePrice, updatedAt }`; no `poolPrice`.                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| 🔴 **`/prices` and `/prices/{symbol}` removed from the published API** `[REST]·[WS]` | Removed | legacy mixed market/collateral price feed → omitted from spec/generated SDK; devnet1 REST compatibility routes may still respond | Delete these consumers. Use `/assetOraclePrices` for asset oracle prices, market summary `markPrice` for perp valuation, and depth/summary mid-price fields for book prices.                                                                                                                                                                                                                                                                                                                                                                                                          |
| 🔴 **Perp market summary aliases removed** `[REST]·[WS]`                             | Removed | `/markets/summary`, `/market/{symbol}/summary` → `/perpMarkets/summary`, `/perpMarket/{symbol}/summary`                          | Replace summary URLs/subscriptions with the `perp*` routes. The unprefixed aliases are not available.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| **`POST /modifyOrder` added** `[REST]·[WS]`                                          | Added   | n/a → new                                                                                                                        | Additive. In-place amend of a resting **LIMIT** order, preserving `orderId`/`clientOrderId` — re-price without cancel/replace. Full-restate with a fresh signature; the modifiable fields are `limitPx`, `qty`, `postOnly`, `expiresAfter`, and `triggerPx`, and all other signed fields are immutable and must match the resting order. Target by `orderId` when present, otherwise by non-zero `clientOrderId`. (The modifiable set is class-split: a `STOP_LOSS`/`TAKE_PROFIT` modify may change only `limitPx` + `triggerPx`.)                                                    |
| **`POST /cancelAllAfter` added** `[REST]·[WS]`                                       | Added   | n/a → new                                                                                                                        | Additive. Account-scoped cancel-on-disconnect (dead-man's switch); `timeoutMs` in `[5000, 60000]`, `0` disarms, last-write-wins; fires on all open orders **except** protective stops.                                                                                                                                                                                                                                                                                                                                                                                                |
| **`GET /wallet/{address}/orderHistory` added** `[REST]`                              | Added   | n/a → new                                                                                                                        | Additive. Account-history endpoint for recent matching-engine order updates, newest-first, capped at 100 rows per request with optional inclusive `startTime` / `endTime` millisecond filters. Rows use the public `Order` shape and include `sequenceNumber`; history is bounded per account and recording excludes accounts under the [current history-eligibility policy](/developers/devnet/api-reference/rest-api-reference.md#retention-and-recording-eligibility). Applications needing a complete order timeline must persist live `orderChanges` and handle gaps explicitly. |
| **`GET /wallet/{address}/transfers` added** `[REST]·[WS]` (`3.4.1`)                  | Added   | n/a → new                                                                                                                        | Additive. Wallet transfer history (account ledger): one `Transfer` entry per account side of every on-chain transfer leg, newest-first, opaque-cursor pagination (`meta.nextCursor`), `type` / time filters. Zero-amount legs are never returned. Net deposits only — `netDepositsAfter` is not `realBalance`. Supersedes the V1 `GET /api/account/{accountId}/transaction-history` and `referral-fee-history` endpoints; the V1 endpoints remain available. The WS channel `/v2/wallet/{address}/transfers` carries the same entries.                                                |
| 🔴 **Depth is bounded** `[REST]·[WS]`                                                | Changed | Perp-aware depth now serves bounded views                                                                                        | REST defaults to 100 levels per side and caps `limit` at 1,000; WS maintains the top 100 per side. Apply boundary removals/additions and do not interpret the feed as a full book. See [REST OpenAPI contract](https://api-devnet.reya-cronos.network/v2/openapi-spec.yaml).                                                                                                                                                                                                                                                                                                          |

### `createOrder` request / response

| Change                                                                             | Type    | Old → New                                                                                                                                                                                                                                                                         | What to do                                                                                                                                                                                                                                                                                                             |
| ---------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🔴 **`symbol` now required**                                                       | Changed | absent from `required` → in `required`                                                                                                                                                                                                                                            | Always send `symbol`. The old implicit-routing path is gone.                                                                                                                                                                                                                                                           |
| 🔴 **`deadline` now required**                                                     | Added   | field did not exist → required `UnsignedInteger` (Unix s), signed into the `Order` envelope                                                                                                                                                                                       | Send a `deadline` (e.g. `now + 30s`) on every create; sign it too (§1).                                                                                                                                                                                                                                                |
| 🔴 **`expiresAfter` semantics reversed**                                           | Changed | "exclusively for PERP IOC and all SPOT orders" (and *mandatory* for those) → coupled to TIF: **GTC/IOC omit it**, **GTT must use `> deadline`**                                                                                                                                   | **Critical for spot integrators:** mainnet *forced* a non-zero `expiresAfter` on every spot order and every perp-IOC; devnet1 *rejects* it for those. Drop it for GTC/IOC; only GTT carries it. See §3.                                                                                                                |
| 🔴 **`clientOrderId` type integer → string**                                       | Changed | `$ref UnsignedInteger` → optional decimal string                                                                                                                                                                                                                                  | Send/parse as a decimal string; uint64 exceeds JS `Number.MAX_SAFE_INTEGER`. Omit the JSON field when you do not want a client tag; provided JSON values must be non-zero. (Applies to the response field too.)                                                                                                        |
| 🔴 **Trigger `timeInForce` required; firing live**                                 | Changed | Omitted TIF / signed GTC → required chosen IOC/GTC/GTT                                                                                                                                                                                                                            | Sign and send the chosen TIF. GTT requires one immutable expiry shared by the armed phase and child, ending early to allow time for settlement. Handle trigger-band errors, OCO consumption, protective sweeps and cancel-only fired children. See [Trigger Orders](/developers/devnet/order-entry/trigger-orders.md). |
| 🔴 **`triggerPx` constraint tightened**                                            | Changed | "only for TP/SL orders" → "required and non-zero for STOP\_LOSS/TAKE\_PROFIT, omitted for LIMIT"                                                                                                                                                                                  | A non-zero `triggerPx` on a LIMIT order is now rejected; SL/TP require a non-zero `triggerPx`.                                                                                                                                                                                                                         |
| **`postOnly` field added**                                                         | Added   | n/a → optional `boolean`                                                                                                                                                                                                                                                          | Additive; defaults false. See §3 for enforcement.                                                                                                                                                                                                                                                                      |
| **`orderId` now required on create responses, issued for IOC, and format changed** | Changed | optional / "generated for all types except IOC", example `123456789-123123123` (hyphenated) → required on `CreateOrderResponse` / "generated for all types including IOC; a no-cross IOC gets an id and status `CANCELLED`", example `490346525705109504` (uint64 decimal string) | Always expect `CreateOrderResponse.orderId`. **Treat `orderId` as an opaque decimal string** — do **not** parse the old hyphenated format (breaking; see §3).                                                                                                                                                          |
| **`cancelReason` + `cancelReasonMessage` added to responses**                      | Added   | n/a → optional cancel metadata on `CreateOrderResponse` / `ModifyOrderResponse` when an order is cancelled                                                                                                                                                                        | Additive. Closed-schema clients should regenerate and treat both fields as optional.                                                                                                                                                                                                                                   |
| **`firstFillId` + `fillCount` added to responses**                                 | Added   | n/a → optional fill-range fields on `CreateOrderResponse` / `ModifyOrderResponse`, present only when the order filled on entry                                                                                                                                                    | Additive. `[firstFillId, firstFillId + fillCount − 1]` is the contiguous fill-ID range the order produced on entry; for an IOC taker (not published to `orderChanges`) the create response is the only place its range is delivered. Regenerate closed bindings; treat as optional.                                    |

### `cancelOrder` / `cancelAll` request / response

| Change                                                                 | Type        | Old → New                                                                                                     | What to do                                                                                                                 |
| ---------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| 🔴 **`cancelOrder` required fields expanded**                          | Changed     | `required: ['signature']` → `['symbol','signature','accountId','nonce','deadline']`                           | Always send `symbol`, `accountId`, `nonce`, `deadline` — the perp-only "assume perp if symbol absent" fallback is removed. |
| 🔴 **`cancelOrder`** **`expiresAfter` → `deadline`**                   | Changed     | `expiresAfter` (spot-only) → `deadline` (required, universal), signed into the `OrderCancel` envelope         | Rename the JSON key and re-sign. (A stray `expiresAfter` is silently ignored, but `deadline` is now required.)             |
| 🔴 **`cancelOrder`** **`clientOrderId` integer → string** (req + resp) | Changed     | `$ref UnsignedInteger` / `type: integer` → `string`                                                           | Send and parse as a decimal string; when used as the target without `orderId`, it must be non-zero.                        |
| 🔴 **`cancelAll` (`MassCancelRequest`) `expiresAfter` → `deadline`**   | Changed     | `required: [...,'expiresAfter']` → `[...,'deadline']`; perp mass-cancel previously unsupported, now supported | Rename the key, re-sign the `MassCancel` envelope; perp mass-cancel now works.                                             |
| **`cancelAll`** **`symbol` now optional, account-wide when omitted**   | Behavioural | omit-behaviour was undocumented → "omit to cancel across all the signer's markets (spot + perp)"              | Provide `symbol` for scoped cancel; omit for account-wide.                                                                 |
| **`MassCancelResponse.cancelledCount` example fixed**                  | Fixed       | example `true` (a boolean bug) → `5`                                                                          | Doc-only.                                                                                                                  |

### Market-data response schemas (shared `MarketSummary` — `[REST]·[WS]`)

| Change                                                                        | Type    | Old → New                                                                                                   | What to do                                                                                         |
| ----------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| 🔴 **`MarketSummary` drops `longOiQty`, `shortOiQty`, `fundingRateVelocity`** | Removed | all three were **required** → removed entirely                                                              | Stop reading them; only the combined `oiQty` remains.                                              |
| 🔴 **`MarketSummary.throttledOraclePrice` → `markPrice`**                     | Changed | `throttledOraclePrice` (AMM/Stork-peg oracle) → `markPrice` (ME-computed, premium-clamped EMA/VWAP + Stork) | Rename the field. **Values differ** — the source changed from the AMM oracle to the ME mark price. |
| 🔴 **`MarketSummary.throttledPoolPrice` → `throttledMidPrice`**               | Changed | `throttledPoolPrice` (AMM pool price) → `throttledMidPrice` (order-book mid)                                | Rename the field; semantics moved from AMM pool to book mid.                                       |

### Execution schemas (shared `PerpExecution` / `SpotExecution` / `ExecutionBust` — `[REST]·[WS]`)

| Change                                                                  | Type        | Old → New                                                                                                                                                                                              | What to do                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🔴 **Execution-bust schema renamed; `reason` now structured**           | Changed     | `SpotExecutionBust(List)` → `ExecutionBust(List)`; `reason` "hex-encoded revert bytes" → `ExecutionBustReason` object union keyed by `reasonName`                                                      | Update `$ref`s / SDK types (unified spot+perp); stop hex-decoding `reason` yourself and branch on `reason.reasonName`. Known contract errors have typed fields (for example `AccountBelowIM` includes `accountId`, `delta`, and `shortfall`); decoded-but-unmodeled errors use `ExecutionBustReasonUnmapped.args`; legacy strings use `DecodedReason.message`; undecodable bytes use `UnknownReason.message`. There is no public `rejectReason` field. |
| 🔴 **`ExecutionBust` taker fields renamed**                             | Changed     | `accountId`→`takerAccountId`, `orderId`→`takerOrderId`                                                                                                                                                 | Rename in every `ExecutionBust` consumer (REST + WS `executionBusts`). Same values, explicit taker perspective.                                                                                                                                                                                                                                                                                                                                        |
| 🔴 **`PerpExecution` taker/maker rename**                               | Changed     | `accountId`→`takerAccountId`, `fee`→`takerFee`, and `openingFee`/`realizedPnl`/`priceVariationPnl`/`fundingPnl` → `taker*` (all required)                                                              | Rename in every `PerpExecution` consumer (REST `/executions` + WS `perpExecutions`). Same values, taker perspective.                                                                                                                                                                                                                                                                                                                                   |
| 🔴 **`SpotExecution` taker fields renamed**                             | Changed     | `accountId`→`takerAccountId`, `orderId`→`takerOrderId`, `fee`→`takerFee`                                                                                                                               | Rename in every `SpotExecution` consumer (REST + WS `spotExecutions`).                                                                                                                                                                                                                                                                                                                                                                                 |
| **`PerpExecution` maker-side fields added**                             | Added       | n/a → `makerAccountId`, `makerOrderId`, `takerOrderId`, `makerFee` (negative = rebate), `makerOpeningFee`, `makerRealizedPnl`, `makerPriceVariationPnl`, `makerFundingPnl` (all maker fields optional) | Use `makerAccountId`/`makerOrderId` to join both sides of an `ORDER_MATCH`. Treat maker fields as optional for every execution because legacy V2 rows may lack them. `ADL` and `MARKET_CLOSE` always omit every `maker*` field; do not require `null`, account `0`, or placeholder PnL values.                                                                                                                                                         |
| **`fillId` added on `SpotExecution`, `PerpExecution`, `ExecutionBust`** | Added       | n/a → optional `string` (a stable fill identifier)                                                                                                                                                     | Additive; a stable id to join an execution/bust to its ME fill and to `orderChanges`. Absent for legacy and non-`ORDER_MATCH` executions.                                                                                                                                                                                                                                                                                                              |
| **Fee v3 execution fields**                                             | Behavioural | `takerFee` represented the legacy taker fee and `makerFee` represented a maker debit/rebate → `takerFee` is the total taker rUSD debit and `makerFee` is omitted on new Fee v3 perp fills              | Make `makerFee` optional. Do not require a zero value; maker fees and rebates are not live in this phase.                                                                                                                                                                                                                                                                                                                                              |
| **`PerpExecution` Fee v3 breakdown added** (`3.4.0`)                    | Added       | n/a → optional `protocolFeeCredit`, `referrerFeeCredit`, `takerRebateCredit`, `poolFeeCredit` (rUSD)                                                                                                   | Additive. Present together on every Fee v3 fill, where `takerFee` is their exact sum; absent together on pre–Fee v3 executions (never synthesized). `takerRebateCredit` is part of the gross debit, not a net credit back to the taker. Regenerate closed models to pick the fields up.                                                                                                                                                                |

### Fee-tier schema (`[REST]·[WS]`)

| Change                                             | Type    | Old → New                                             | What to do                                                                                                                         |
| -------------------------------------------------- | ------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| 🔴 **`FeeTierParameters.volume14d` → `volume30d`** | Changed | required 14-day threshold → required 30-day threshold | Rename the field and update any fee-tier display, alert, or threshold logic. Regenerate closed models from the current API bundle. |

### Order-change stream & open-orders shape (`[REST]·[WS]`)

| Change                                                             | Type        | Old → New                                                                                                                                                                           | What to do                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------------------------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`Order` gains `postOnly` + `expiresAfter`**                      | Added       | n/a → `postOnly` (boolean), `expiresAfter` (Unix s; present only for GTT orders, absent otherwise)                                                                                  | Additive; both can change on `modifyOrder`; response payloads use absence, not `0`, for non-GTT order lifetime.                                                                                                                                                                                                                                                                    |
| **`Order` gains `clientOrderId`**                                  | Added       | n/a → optional decimal string, present only when the order has a non-zero client id                                                                                                 | Additive; use it to reconcile open-orders snapshots and `orderChanges` with client-side ids.                                                                                                                                                                                                                                                                                       |
| **`Order` gains `firstFillId` + `fillCount`**                      | Added       | n/a → optional `firstFillId` (string, fill identifier) + `fillCount`, present only on fill updates                                                                                  | Additive; identifies the contiguous fill-ID range `[firstFillId, firstFillId + fillCount − 1]` an order update produced. Use it to join order updates to `fillId`-keyed executions. Absent for non-fill updates and resting snapshots.                                                                                                                                             |
| **`Order` gains `sequenceNumber` on event rows**                   | Added       | n/a → optional matching-engine order-event sequence on `orderHistory` REST rows and live `orderChanges` WS rows                                                                     | Additive. Use it to splice REST `orderHistory` with the live `orderChanges` stream and to de-duplicate inclusive `orderHistory` page-boundary overlap. It is omitted on open-order/resting snapshots, including `GET /openOrders` and the `orderChanges` subscribe snapshot.                                                                                                       |
| **`orderChanges` subscribe ack gains `snapshotSequenceNumber`**    | Added       | subscribe ack contents were only the open-order snapshot → `OrderChangesSnapshot { data, snapshotSequenceNumber }`                                                                  | Additive. On reconnect, rebuild from `contents.data`; subsequent live `orderChanges` rows have `sequenceNumber` greater than `contents.snapshotSequenceNumber`.                                                                                                                                                                                                                    |
| **`Order` gains `cancelReason` + `cancelReasonMessage`**           | Added       | n/a → optional cancel metadata on cancelled order updates                                                                                                                           | Additive; use `cancelReason` to distinguish user/mass/COD cancels from engine outcomes such as IOC remainder, no liquidity, self-trade prevention, or GTT expiry.                                                                                                                                                                                                                  |
| **`modifyOrder` + COD events flow through `orderChanges`**         | Added       | n/a → a `modifyOrder` emits an `orderChanges` event with the updated `Order` (same `orderId`); a COD fire emits a burst of per-order `CANCELLED` events (protective stops excluded) | Additive — consumers tracking order state get these automatically.                                                                                                                                                                                                                                                                                                                 |
| **Trigger orders appear on `orderChanges` with their actual type** | Behavioural | trigger orders not distinguished → `orderType = STOP_LOSS` / `TAKE_PROFIT`                                                                                                          | Armed triggers are account-visible, re-priceable, and cancellable (see [Trigger Orders (SL/TP)](/developers/devnet/order-entry/trigger-orders.md)); they are outside the book while armed and fire into the selected TIF. Read `triggered` on `openOrders` / `orderChanges` to distinguish the phases; it is absent and unknown on `orderHistory`. Fired children are cancel-only. |
| 🔴 **Pre-trade risk enforced**                                     | Behavioural | Pre-trade margin checks are enforced; fills also require successful settlement                                                                                                      | Handle rejected creates/modifies separately from `RISK_CANCELLED` on an existing maker. Pending fills consume risk capacity; accepted resting quotes are not a margin reservation or a settlement guarantee. See [Pre-trade risk](/developers/devnet/risk/margin-system.md#pre-trade-risk-checks).                                                                                 |
| **Partial fills surface as `status = OPEN`**                       | Behavioural | An order with an unfilled remainder stays `OPEN`; public `OrderStatus` is `OPEN` / `FILLED` / `CANCELLED`                                                                           | Distinguish partial fills by `cumQty > 0`, not a separate status (consistent with mainnet).                                                                                                                                                                                                                                                                                        |

### Shared enums and error codes (`[REST]·[WS]`)

| Change                                                  | Type    | Old → New                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🔴 **`OrderType` renamed**                              | Changed | `["LIMIT","TP","SL"]` → `["LIMIT","STOP_LOSS","TAKE_PROFIT"]` — replace `"TP"`/`"SL"` everywhere they appear (createOrder, order history, `orderChanges`, executions).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| **`TimeInForce` gains `GTT`**                           | Added   | `["IOC","GTC"]` → `["IOC","GTC","GTT"]`. Closed-enum consumers must add a `GTT` branch.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| 🔴 **`ExecutionType.DUST` renamed**                     | Changed | `DUST` → `MARKET_CLOSE`. Closed-enum consumers generated from the previous devnet API must replace the branch. `MARKET_CLOSE` is perp-only and is never emitted for spot.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| 🔴 **`OrderStatus.REJECTED` removed**                   | Removed | `["OPEN","FILLED","CANCELLED","REJECTED"]` → `["OPEN","FILLED","CANCELLED"]`. Request-level rejects such as post-only-would-cross now use `RequestErrorCode` (`POST_ONLY_WOULD_CROSS_ERROR`) over REST/ws-exec, not a successful order status. Cancelled accepted outcomes use `CANCELLED` plus optional `cancelReason`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| 🔴 **GET/read `RequestErrorCode` names suffixed**       | Changed | `SYMBOL_NOT_FOUND`, `NO_ACCOUNTS_FOUND`, `NO_PRICES_FOUND_FOR_SYMBOL` → `SYMBOL_NOT_FOUND_ERROR`, `NO_ACCOUNTS_FOUND_ERROR`, `NO_PRICES_FOUND_FOR_SYMBOL_ERROR`. This completes the public `_ERROR` suffix convention already used by order-entry failures.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **`RequestErrorCode` gains order-entry and risk codes** | Added   | Baseline perpOB adds `CANCEL_ALL_AFTER_OTHER_ERROR`, `ORDER_NOT_FOUND_ERROR`, `POST_ONLY_WOULD_CROSS_ERROR`, `MODIFY_QTY_BELOW_FILLED_ERROR`, `EMPTY_MODIFY_ERROR`, `MODIFY_ORDER_OTHER_ERROR`. The next contract slice adds `RATE_LIMITED_ERROR`, `INSUFFICIENT_BALANCE_ERROR`, `OPEN_ORDER_CAP_ERROR`, `PRICE_QTY_BOUNDS_ERROR`, `SERVICE_DISABLED_ERROR`, `UNAUTHORIZED_ACCOUNT_ERROR`, `TRADING_HALTED_ERROR`, `DUPLICATE_CLIENT_ORDER_ID_ERROR`. Pre-trade risk, trigger, and expiry add `ACCOUNT_BELOW_LIQUIDATION_MARGIN_ERROR`, `ACCOUNT_BELOW_INITIAL_MARGIN_ERROR`, `OPEN_INTEREST_CAP_ERROR`, `OPEN_INTEREST_BUDGET_ERROR`, `REDUCE_ONLY_CONDITION_NOT_MET_ERROR`, `CROSSING_ORDERS_TEMPORARILY_UNAVAILABLE_ERROR`, `TRIGGER_ALREADY_EXISTS_ERROR`, `TRIGGER_IOC_MUST_NOT_EXPIRE_ERROR`, `TRIGGER_LIMIT_OUTSIDE_BAND_ERROR`, and `ORDER_EXPIRES_TOO_SOON_ERROR` (the GTC-only trigger guard is retired). `*_OTHER_ERROR` remains possible as a per-operation ME catch-all (read `message`). **`POST_ONLY_WOULD_CROSS_ERROR` is a request failure** — REST returns an error response and ws-exec returns `{ ok: false, error }`; it is not a successful `status: CANCELLED` outcome. |
| **`CancelReason` enum added**                           | Added   | `NO_LIQUIDITY`, `IOC_REMAINDER`, `SELF_TRADE_PREVENTION`, `GTT_EXPIRED`, `USER_CANCEL`, `MASS_CANCEL`, `CANCEL_ALL_AFTER`, `RISK_CANCELLED`, `FEED_RESET`, `OCO_SIBLING_FIRED`, `PROTECTIVE_SELF_TRADE_SWEEP`, `POSITION_CLOSED`, and `RISK_REJECTED`. `RISK_CANCELLED` means pre-trade risk cancelled an already-resting order; admission failures use `RequestErrorCode` instead. Risk cancellation messages are fixed per reason, not the specific failed check. Protective firing can consume both legs without a fill; see [Trigger Orders](/developers/devnet/order-entry/trigger-orders.md#firing-oco-and-self-trades). `FEED_RESET` remains in API 3.4.0, but current devnet1 uses close code `1012` to request resynchronization. If you receive `FEED_RESET`, rebuild the order view; it does not mean the orders were cancelled. Closed-enum consumers should regenerate before relying on cancel metadata.                                                                                                                                                                                                                                                                         |
| **New schemas**                                         | Added   | `ModifyOrderRequest/Response`, `CancelAllAfterRequest/Response`, `ExecutionBust(List)` (replacing `SpotExecutionBust(List)`), `ExecutionBustReason`, `AssetOraclePrice`, `OrderHistoryList`, `OrderChangesSnapshot`, and optional cancel metadata on `Order` / create / modify responses. Regenerate SDK types.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| 🔴 **`Price` schema removed**                           | Removed | `Price { symbol, oraclePrice, poolPrice, updatedAt }` → removed                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

`CancelReason` precedence: if an IOC partially fills and its remaining quantity is then cancelled by self-trade prevention, the response keeps the fill quantities and reports `cancelReason: SELF_TRADE_PREVENTION`. `IOC_REMAINDER` only covers remaining IOC quantity cancelled because matching stopped without a self-cross.

### Unchanged (no migration)

`AccountBalance` (still `realBalance` + `balanceDEPRECATED`), `Position`, `Depth`, `MarketDefinition`, `WalletConfiguration`, `CandleHistoryData`, and `Side` (`B`/`A`) are unchanged in shape. Depth retains its wire shape but now represents the bounded view above. Later additive price and fee fields are covered in the [Changelog](/developers/devnet/changelog/changelog.md).

***

## 3. Behavioural changes (same endpoint/field, new runtime behaviour)

### 🔴 BREAKING — `postOnly` is now enforced

A `postOnly` GTC/GTT order that would cross the best opposite price at insertion is **rejected** with `POST_ONLY_WOULD_CROSS_ERROR` before any fill. REST returns an error response and ws-exec returns `{ ok: false, error }`. On mainnet the flag did not exist / was not enforced. **What to do:** only submit post-only orders that would rest; do not retry at the same price. Do not combine `postOnly` with IOC (`INPUT_VALIDATION_ERROR`).

### 🔴 BREAKING — `expiresAfter` is now TIF-coupled (reversal for spot + perp-IOC)

GTC and IOC **omit `expiresAfter`**; GTT **must** send a timestamp `> deadline`. **Mainnet&#x20;*****required*****&#x20;a non-zero `expiresAfter` for every spot order and every perp-IOC** — devnet1 rejects exactly that. This is a guaranteed break for any existing spot or perp-IOC integrator. **What to do:** drop the previously-mandatory timestamp for GTC/IOC; carry `expiresAfter` only on GTT.

### 🔴 BREAKING — `orderId` format: hyphenated → decimal string

Old example `123456789-123123123` (legacy hyphenated) → `490346525705109504` (uint64 decimal string). Applies to `Order.orderId`, `CreateOrderResponse.orderId`, `CancelOrderResponse.orderId`, `ModifyOrderResponse.orderId`. **What to do:** treat `orderId` as an opaque string and compare by string equality — do **not** parse the hyphenated format and do **not** coerce to a JS `Number` (overflows `Number.MAX_SAFE_INTEGER`). The same id format joins `orderChanges` to create/cancel/modify responses and to `fillId`-keyed executions.

### Behavioural (non-breaking) — `CreateOrderResponse.orderId` is always assigned

A no-cross IOC still receives an engine-assigned id and is returned with status `CANCELLED`; `CreateOrderResponse.orderId` is required in the schema. **What to do:** if your code allowed create responses without `orderId`, update it.

### Behavioural (non-breaking) — perp mass-cancel now supported

`cancelAll` on a perp market previously returned a not-supported error; it now works via the unified `marketId` namespace. Remove any guard that blocked perp `cancelAll`.

***

## Quick migration checklist

1. **Rebuild the order signer** to the flat `Order`/`OrderDetails` envelope: `orderType` `0`/`1`/`2`, signed `postOnly`, and the two-field time model (`deadline` + `expiresAfter`); cancel family uses `uint64`. Point `verifyingContract` at the devnet1 `OrdersGateway`. ([Signatures & Nonces](/developers/devnet/authentication/signatures-and-nonces.md))
2. **Add `symbol` + `deadline`** to every create; omit `expiresAfter` for GTC/IOC; carry it only on GTT.
3. **Send/parse `clientOrderId` as a string**; treat `orderId` as an opaque decimal string.
4. **Add `symbol`/`accountId`/`nonce`/`deadline`** to `cancelOrder`; rename `expiresAfter` → `deadline` on `cancelOrder`/`cancelAll`.
5. **Rename `TP`/`SL` → `TAKE_PROFIT`/`STOP_LOSS`**; add `GTT`, replace `DUST` with `MARKET_CLOSE`, remove `OrderStatus.REJECTED`, and support the current error codes and optional cancel-reason fields.
6. **Move bust URLs/schemas** to `/executionBusts` + `ExecutionBust(List)`; treat `ExecutionBust.reason` as a structured object and branch on `reason.reasonName`; stop hex-decoding `reason`.
7. **Rename execution taker fields** to `taker*` (`PerpExecution` and `SpotExecution`); pick up `fillId` + optional maker-side fields on executions, and `firstFillId` + `fillCount` on order responses / `orderChanges`. Treat maker fields as optional for all executions, accept all of them being absent on `ADL` and `MARKET_CLOSE`, and make `makerFee` optional for new Fee v3 perp fills. From `3.3.0`, read the optional `protocolFeeCredit` / `referrerFeeCredit` / `takerRebateCredit` / `poolFeeCredit` breakdown on Fee v3 perp fills.
8. **Drop `MarketSummary.longOiQty/shortOiQty/fundingRateVelocity`**; rename `throttledOraclePrice → markPrice`, `throttledPoolPrice → throttledMidPrice`.
9. **Handle enforced `postOnly`, live SL/TP firing and pre-trade risk.** Send the chosen trigger TIF; allow time for GTT settlement; track OCO consumption and cancel-only children. Handle admission errors and `RISK_CANCELLED` separately. See [Margin System](/developers/devnet/risk/margin-system.md#pre-trade-risk-checks).
10. **Replace removed AMM-era reference endpoints:** use `/v2/perpMarketDefinitions` instead of `/v2/marketDefinitions`, and remove `/v2/liquidityParameters` / `LiquidityParameters` consumers.
11. **Replace unprefixed perp market summary aliases** with `/v2/perpMarkets/summary` and `/v2/perpMarket/{symbol}/summary`; replace any `/v2/prices` consumers with `/v2/assetOraclePrices` for asset Stork oracle prices, market summary `markPrice` for perp valuation, and depth/summary fields for book prices.
12. **Optionally adopt** `orderHistory` for account-history views and support tooling, and use `Order.sequenceNumber` / `snapshotSequenceNumber` to splice available history with the live `orderChanges` stream. For a complete order timeline, persist the live stream and handle gaps explicitly; REST history has [retention and recording limits](/developers/devnet/api-reference/rest-api-reference.md#retention-and-recording-eligibility).
13. **Replace `FeeTierParameters.volume14d` with `volume30d`** and update 14-day fee-tier assumptions.
14. **Optionally adopt** `modifyOrder` and `cancelAllAfter`.

***

*This page is a perpOB cutover aid with a shelf life; the dated, authoritative record of changes is the* [*Changelog*](/developers/devnet/changelog/changelog.md)*.*
