Skip to content

Release 20260223

Changes relevant to 3rd-party integrators between releases release-all-20260212 (12 Feb 2026) and release-all-20260223 (23 Feb 2026).

Summary

ActionChange TypeBreaking?Action Required
All order-returning actionsResponse changePossiblyMigrate from flat orderId/id to composite order object
getBalanceUpdatesResponse changeYesUpdate id parsing from number to string; handle new TRANSFER filter
createSubaccountBehavior changeYesDelegated signers can no longer call this action
transferCollateralBehavior changePossiblyFailed transfers now return HTTP 400/500 instead of 200
getTradesResponse changePossiblyclientOrderId field removed (was always empty)
All actionsBehavior changePossibly500 error responses no longer include internal error details
getTransfersNew endpointNoNone
cancelAllOrders (REST)New endpointNoNone
cancelAllOrdersBehavior changeNoNone
Trade subscription (WS)Response changeNoNone
Internal refactoringInternal onlyNoNone

All changes are available on both REST (POST /v1/trade) and WebSocket (/v1/ws/trade) unless otherwise noted.


getBalanceUpdates — response change

The id field type has changed and the TRANSFER action filter has been added.

What you need to do

  1. Update your id field parsing: it is now a JSON string (e.g., "12345") instead of a JSON number (e.g., 12345).
  2. If you filter by actionFilter, you may now also use "TRANSFER" to retrieve inter-subaccount transfer records.
  3. If you validate error messages for invalid actionFilter values, update your matching.

Response field changes

FieldBeforeAfterNotes
idint64 (JSON number)string (JSON string)Breaking: 12345"12345"
action"DEPOSIT" or "WITHDRAWAL""DEPOSIT", "WITHDRAWAL", or "TRANSFER"New value
fromSubAccountId(not present)string (optional)New field; present for TRANSFER records

Error message change

  • Before: "actionFilter must be 'DEPOSIT', 'WITHDRAWAL', or comma-separated combination"
  • After: "invalid action filter, available filters: DEPOSIT, WITHDRAWAL, TRANSFER"

Response example

{
  "status": "ok",
  "response": {
    "balanceUpdates": [
      {
        "id": "42",
        "subAccountId": "1867542890123456789",
        "action": "TRANSFER",
        "status": "success",
        "amount": "100.00",
        "collateral": "USDT",
        "timestamp": 1740300000000,
        "fromSubAccountId": "1867542890123456788",
        "toSubAccountId": "1867542890123456789"
      }
    ]
  }
}

Errors

HTTP StatusConditionError Code
400Invalid actionFilter valueVALIDATION_ERROR
400Invalid limit or offsetVALIDATION_ERROR
500Internal service failureINTERNAL_ERROR

createSubaccount — behavior change

The createSubaccount action now requires the wallet owner.

What you need to do

If you use delegated signers to create subaccounts, move this call to the wallet owner. Delegated signers will now receive an authorization error.

What changed

createSubaccount was added to the RequiresOwner() list alongside transferCollateral, withdrawCollateral, removeAllDelegatedSigners, and updateSubAccountName. Requests signed by a delegated signer are now rejected.


Composite OrderId — response change across all order-returning actions

Every API response that previously returned order identifiers as flat strings now returns a composite object alongside the deprecated flat field. This affects: placeOrders, cancelOrders, cancelAllOrders, modifyOrder, getOpenOrders, getOrderHistory, getTrades, getPositions, and all WebSocket order/trade event notifications.

What you need to do

Migrate from the deprecated flat fields to the new composite order object. The deprecated fields remain present during the transition period but will be removed in a future release.

Field mapping:
EndpointOld field (deprecated)New field
placeOrdersstatuses[].resting.id / filled.id / canceled.id.resting.order / .filled.order / .canceled.order
cancelOrdersstatuses[].canceled.idstatuses[].canceled.order
cancelAllOrders[].orderId[].order
modifyOrderorderIdorder
getOpenOrdersorderId, takeProfitOrderId, stopLossOrderIdorder, takeProfitOrder, stopLossOrder
getOrderHistoryorderIdorder
getTradesorderIdorder
getPositionstakeProfitOrderIds, stopLossOrderIdstakeProfitOrders, stopLossOrders
WS order/trade eventsorderIdorder

New OrderId object shape

{ "venueId": "1948058938469519360", "clientId": "my-order-1" }
FieldTypeDescription
venueIdstringSystem-generated order identifier (definitive)
clientIdstringOptional client-provided identifier; empty string if not set

Notes

  • getOpenOrders: TP/SL fields are now nullable. When no TP/SL is attached, takeProfitOrder and takeProfitOrderId are omitted entirely (previously takeProfitOrderId could be "0").
  • getPositions: TP/SL are now arrays of composite objects alongside the deprecated arrays of flat strings.

transferCollateral — behavior change

Failed transfers now return proper HTTP error responses instead of HTTP 200 with failure status in the body.

What you need to do

Handle HTTP 400/500 for transfer failures. Successful transfers still return HTTP 200.

Error code mapping:

Error CodeHTTP Status
ASSET_NOT_FOUND, INSUFFICIENT_MARGIN, INVALID_VALUE, VALIDATION_ERROR400
All others500

getTrades — response change

The clientOrderId field has been removed. It was always an empty string. The client order ID is now available via the composite order.clientId field.


500 error response sanitization — behavior change across all actions

HTTP 500 error responses no longer include raw internal error messages or the details map. Messages are now generic (e.g., "Failed to cancel all orders"). If you parse message text or details.error, update your matching.


Non-breaking changes

  • getTransfers — New endpoint. Query inter-subaccount transfer history with optional filtering by symbol, startTime, endTime, and pagination via limit/offset. Returns { transfers: [...], total: N }. Available on both REST and WebSocket.

  • cancelAllOrders (REST) — Now registered on the REST POST /v1/trade handler in addition to WebSocket.

  • cancelAllOrders — Wildcard support. Pass symbols: ["*"] to cancel orders across all markets. The "*" must be the sole element.

  • Trade subscription (WebSocket) — Notifications now include "channel": "trade" and "timestamp" (milliseconds) at the top level.

  • Internal refactoring — Error codes and categories moved to lib/core/status_codes. No wire-format impact.


Release 20260206

Changes relevant to 3rd party integrators across releases release-all-20260205 (Feb 5) and release-all-20260206 (Feb 6).

Summary

ActionChange TypeBreaking?Action Required
getSubAccountsNew endpointNoImplement if needed
removeAllDelegatedSignersNew endpointNoImplement if needed; requires new EIP-712 type
withdrawCollateralBehavior changePossiblyUpdate error message parsing if you match on fee errors
getTrades, placeOrdersInternal onlyNoNone. Wire format unchanged.

All changes are available on both REST (POST /v1/trade) and WebSocket (/v1/ws/trade).


getSubAccounts — new endpoint

Returns all subaccounts under the authenticated user's master account. Works for both owners and delegates. Each subaccount includes its delegated signers and fee rate tier.

What you need to do

Sign using the existing SubAccountAction EIP-712 type (same as getSubAccount) with action set to "getSubAccounts". No nonce required (read-only action).

Request

Identical to getSubAccount. The only difference is params.action:

{ "params": { "action": "getSubAccounts" } }

No additional parameters.

Response

Returns { subAccounts: [...] } where each element has the same shape as an existing getSubAccount response, plus two additional fields: feeRates and delegatedSigners.

New fields on each subaccount object (compared to getSubAccount):
FieldTypeDescription
feeRatesFeeRateInfoFee tier for this subaccount
delegatedSignersDelegatedSigner[]All delegates authorized on this subaccount. Empty [] if none exist.
FeeRateInfo fields:
FieldTypeExampleDescription
makerFeeRatestring"0.0002"Maker fee rate (decimal)
takerFeeRatestring"0.0005"Taker fee rate (decimal)
tierNamestring"Regular User"Fee tier name
DelegatedSigner fields:
FieldTypeExampleDescription
subAccountIdstring"2011391943438766080"Subaccount this delegation applies to
walletAddressstring"0xDelegateWalletAddress"Delegate's Ethereum address
permissionsstring[]["trading"]Granted permissions
expiresAtnumber | null1738800000000Expiry as unix ms, or null for no expiration

Errors

HTTP StatusConditionError Code
400Missing subaccountIdVALIDATION_ERROR
500Internal service failureINTERNAL_ERROR

removeAllDelegatedSigners — new endpoint

Atomically removes all delegated signers from a subaccount. Owner-only: delegates receive a 403 error.

What you need to do

Add the new RemoveAllDelegatedSigners EIP-712 primary type to your signing code. This is a write action, so nonce is required.

New EIP-712 type to add:
RemoveAllDelegatedSigners: [
  { name: "subAccountId", type: "uint256" },
  { name: "nonce",        type: "uint256" },
  { name: "expiresAfter", type: "uint256" }
]

Use the standard EIP-712 domain (same as all other actions). Sign with primaryType: "RemoveAllDelegatedSigners".

Request

params contains only action — no additional action-specific fields:

{
  "params": { "action": "removeAllDelegatedSigners" },
  "subaccountId": "123",
  "nonce": 1738700000000,
  "signature": { "v": 27, "r": "0x...", "s": "0x..." },
  "expiresAfter": 1738700060000
}
FieldTypeRequiredDescription
params.actionstringYesMust be "removeAllDelegatedSigners"
subaccountIdstringYesSubaccount to remove all delegates from
noncenumberYesTimestamp in ms (replay protection)
signatureobjectYesEIP-712 signature { v, r, s }
expiresAfternumberNoRequest expiry as unix ms. 0 = no expiry

Response

{
  "status": "ok",
  "response": {
    "subAccountId": "123",
    "removedSigners": ["0xDelegateAddr1", "0xDelegateAddr2"]
  }
}
FieldTypeDescription
subAccountIdstringThe subaccount from which signers were removed
removedSignersstring[]Wallet addresses of removed delegates. Empty [] if none

Errors

HTTP StatusConditionError Code
400Invalid format or validation failureVALIDATION_ERROR
403Caller is a delegate, not the ownerVALIDATION_ERROR
404Subaccount not foundNOT_FOUND
500Internal service failureINTERNAL_ERROR

withdrawCollateral — behavior change

What you need to do

If you parse or pattern-match on withdrawal fee error messages, update your matching. Request and response schemas are unchanged.

What changed

Withdrawal fees are now per-asset in native units instead of a flat $5 USD. The minimum withdrawal amount must exceed the asset-specific fee.

AssetFeeApproximate USD equivalent
USDT5$5
WETH0.0017~$5 at ~$3,000/ETH
BTC0.00005~$5 at ~$100,000/BTC

Error message format change

  • Before: "withdrawal amount must be greater than $5 fee"
  • After: "withdrawal amount must be greater than {fee} {asset} fee"

Examples of the new format:

  • "withdrawal amount must be greater than 5 USDT fee"
  • "withdrawal amount must be greater than 0.0017 WETH fee"
  • "withdrawal amount must be greater than 0.00005 BTC fee"

Net amount

The on-chain withdrawal amount remains amount - fee. If you display estimated net amounts to users, note that the fee now varies by asset. Withdrawal fees are not yet exposed via a public API endpoint.