Skip to content

Changelog

This page tracks significant changes to the Synthetix API documentation and structure.

17 March 2026

Changes relevant to 3rd-party integrators for the release of Synthetix on 2026-03-17.

Summary

ActionChange TypeBreaking?Action Required
WebSocket id / requestIdBehavior changePossiblyEnsure id and requestId are JSON strings, not numbers
clientOrderIdBehavior changeYesRemove leading/trailing whitespace from client order IDs
triggerPriceTypeBehavior changePossiblyUse exactly "mark" or "last" (case-sensitive, no trim)
Request body sizeBehavior changePossiblyKeep request bodies ≤ 20 KB or handle HTTP 413
createSubaccount nameBehavior changePossiblyReject names with leading/trailing whitespace
Time params conflictBehavior changePossiblyDo not send both startTime and fromTime (or endTime and toTime)
Rate limitsBehavior changePossiblyExpect updated limits; see deployment config
cancelOrders by cloidNew capabilityNoOptional: use clientOrderIds instead of venueOrderIds
modifyOrder by cloidNew capabilityNoOptional: use clientOrderId instead of venueOrderId
getPositionHistoryNew endpointNoQuery closed position history
getCollaterals (info)New endpointNoFetch collateral configs from info API
getDelegationsForDelegateResponse changeNoNew accountValue field per delegation
Order rejectionsResponse changeNoNew errorCode field for programmatic handling
Collateral tiersResponse changeNoNew valueAddition, valueRatio in tier objects
Candle timeframesNew capabilityNo30m, 8h, 12h, 3d, 3m supported

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

WebSocket id / requestId — Behavior change

WebSocket requests must send id and requestId as JSON strings. Numeric values are rejected.

What you need to do

  • Ensure id and requestId in outgoing WebSocket messages are strings, e.g. "id": "req-123" not "id": 123.
  • Handle HTTP 400 with message "The id must be a string" or "The requestId must be a string" if your client sends numbers.

Error message change

  • Before: "Invalid JSON"
  • After: "The id must be a string" or "The requestId must be a string" when the parse failure is due to numeric id/requestId.

clientOrderId — Behavior change

Leading and trailing whitespace in clientOrderId is now rejected instead of trimmed.

What you need to do

  • Strip whitespace from clientOrderId before sending. Values like " my-order-1 " will return HTTP 400.
  • Validate that your client order IDs contain no leading or trailing spaces.

triggerPriceType — Behavior change

triggerPriceType now accepts only exact matches: "mark" or "last". Case-insensitive matching and trimming are no longer applied.

What you need to do

  • Send exactly "mark" or "last". Values like "MARK", " Mark ", or "LAST" will be rejected.
  • Empty string still defaults to "mark".

Request body size limit — Behavior change

Request bodies larger than 20 KB are rejected with HTTP 413.

What you need to do

  • Keep request bodies under 20 KB. For large batches, split into multiple requests.
  • Handle HTTP 413 and "Request body too large" in error responses.

createSubaccount name validation — Behavior change

Subaccount names with leading or trailing whitespace are rejected.

What you need to do

  • Trim subaccount names before sending. Values like " My Account " will return HTTP 400 with a message about leading or trailing whitespace.

Time query params conflict — Behavior change

Endpoints that support time ranges now enforce consistency. Do not send both startTime and fromTime, or both endTime and toTime, with different values.

What you need to do

  • Prefer startTime and endTime. fromTime and toTime remain supported but are deprecated.
  • Do not send conflicting pairs (e.g. startTime and fromTime with different values). This returns HTTP 400.

Non-breaking changes

  • **cancelOrders** — Can cancel by clientOrderIds instead of venueOrderIds. Same action, new optional param shape.
  • **modifyOrder** — Can modify by clientOrderId instead of venueOrderId. Same action, new optional param shape.
  • **getPositionHistory** — New endpoint. Query closed position history with symbol, startTime, endTime, limit, offset.
  • **getCollaterals** — New info endpoint. Returns collateral configs with tiers. Replaces subaccount-specific collateral fetch.
  • **getDelegationsForDelegate** — New accountValue field (string, USDT) per delegated account.
  • Order rejections — New errorCode field for place/cancel/modify responses. See Error Handling for codes and retry policy.
  • Collateral tiers — New valueAddition and valueRatio fields in tier objects.
  • Candle timeframes — Added 30m, 8h, 12h, 3d, 3m to getCandles.
  • Rate limits — Updated values; consult deployment configuration for current limits.

16 March 2026

getSubAccountIds — delegation support added

The getSubAccountIds endpoint now accepts an optional includeDelegations boolean parameter.

  • Default behavior unchanged: omitting includeDelegations (or setting it to false) returns a flat array of owned subaccount IDs, identical to the previous response.
  • New: when includeDelegations: true, the response is an object with two fields:
    • subAccountIds — subaccount IDs owned by the wallet address
    • delegatedSubAccountIds — subaccount IDs for which the wallet address has been granted delegate access

This applies to both the REST (/v1/info) and WebSocket (/v1/ws/info) interfaces.

No change to request or response format for callers that do not send includeDelegations.

16 March 2026

getMarketPrices — field documentation corrected

The fundingRate and timestamp fields in the getMarketPrices response are now correctly documented. Previous documentation incorrectly stated these fields return zero / empty values. Both fields are populated:

  • fundingRate: Returns the current estimated funding rate for each market, sourced from the funding rate service.
  • timestamp: Returns a Unix millisecond timestamp representing when the response was generated.

No change to request format or response structure. This is a documentation correction only.


14 March 2026

updateSubAccountName — new endpoint documented

Added documentation for the updateSubAccountName endpoint, which allows renaming an existing trading subaccount. Available on both REST (POST /v1/trade) and WebSocket (/v1/ws/trade).

  • Request: Requires subAccountId and name parameters with EIP-712 signature
  • Response: Returns the updated subAccountId and name
  • Authentication: Subaccount-level EIP-712 signing with UpdateSubAccountName typed data

18 March 2026

getSubAccount — collateral value fields now populated

The collaterals array in the getSubAccount response now includes adjustedCollateralValue and collateralValue for each collateral entry. These fields were always present in the response schema but previously returned empty strings. They now return the USD-equivalent collateral values:

  • collateralValue: full collateral value before haircut
  • adjustedCollateralValue: collateral value after applying the haircut discount

This applies to both the REST and WebSocket variants of getSubAccount, as well as getSubAccounts.

No change to request format or other response fields.

10 March 2026

cancelAllOrders — response docs corrected

The message field in cancelAllOrders response items is populated only when a cancellation fails for a specific order. On success, message is an empty string. Previous documentation incorrectly showed "Order cancelled successfully" as the success message value.

The symbol field (the market symbol for the cancelled order) is now documented in both the REST and WebSocket response examples, consistent with the actual response contract.

No change to request format, signing, or other response fields.

11 March 2026

Changes relevant to 3rd-party integrators for the release of Synthetix on 2026-03-11.

Summary

ActionChange TypeBreaking?Action Required
addDelegatedSignerBehavior changeYesSend exactly one permission value and update delegation-management flows for real delegate vs session semantics.
removeDelegatedSignerBehavior changeYesExpect hierarchical removal rules and owner-side cascade deletion of child session signers.
All WebSocket channels excl. orderbookUpdateBehavior changePossiblyNew seq field added to WebSocket channels: update parsers to accept new fields; implement gap detection using seq.
requestId / X-Request-ID / WebSocket idBehavior changePossiblySend an explicit valid client request ID if you rely on echoed correlation IDs; stop assuming the server always generates one in the payload.
createSubaccountBehavior changePossiblyTreat tier-limit and validation failures as structured 400 errors, not generic retriable 500s.
cancelOrdersResponse changePossiblyRemove any client workaround that interpreted error items as successful cancellations.
cancelAllOrdersResponse changePossiblyUpdate response parsing of the message field, which is now error-only; success returns empty/null.
orderbookUpdateBehavior changeYesOrderbook now supports opt-in diff mode: subscribe per symbol, not ALL, and either request format: "snapshot" or implement diff merge with meseq/prevMeseq/checksum validation.
candleUpdateResponse changePossiblyHandle an initial salutation payload before candle OHLC updates.
subAccountUpdateResponse changePossiblyTolerate seq/provenance fields and resync from authoritative state when needsResync=true.
getSubAccountIdsResponse changeNoNo change unless you opt into includeDelegations=true; then parse an object instead of a flat array.
getDelegatedSigners / getDelegationsForDelegateResponse changeNoAllow additive delegation fields such as addedBy, cascadeRemovedSigners, and optional owningAddress.
createSubaccount / getSubAccount / getSubAccountsResponse changeNoAllow the additive accountLimits.maxSubAccounts field.
placeOrdersResponse changeNoIf you decode status rows strictly, allow order on rejected status items.
marketPriceUpdate / tradeResponse changeNoAllow additive sequencing, funding-rate, and provenance fields on websocket notifications.

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

addDelegatedSigner — behavior change

Delegation permissions are now enforced as real permission tiers instead of loosely treated aliases.

What you need to do:

  • Send exactly one permission in permissions.
  • Use delegate for manager-style signers and session for trading/session keys.
  • Do not rely on delegates or session signers being able to create arbitrary downstream delegations.

Permission changes

FieldBeforeAfterNotes
permissionsAny non-empty array was broadly tolerated in practiceExactly one value is requiredMulti-value arrays are rejected.
permissions[0]Semantically loosedelegate, session, or legacy trading onlytrading remains accepted for compatibility but is normalized to session.
Delegation authorityLoose / flattened behaviorHierarchicalOwner can create delegate or session; delegate can create/manage only session signers it created; session signer cannot create delegations.

removeDelegatedSigner — behavior change

Delegation removal now follows the same hierarchy as delegation creation, and owner removals can cascade.

What you need to do:

  • Re-test any flow where a delegate or session signer removes other signers.
  • If you remove a delegate as the owner, handle the optional cascadeRemovedSigners list.

Removal changes

FieldBeforeAfterNotes
Removal authorityLooser behaviorHierarchicalDelegate can remove only session signers it created; session signer cannot remove delegations.
ResponsesubAccountId, walletAddressAdds optional cascadeRemovedSignersReturned when owner removal also deletes child session signers created by the removed delegate.

requestId / X-Request-ID / WebSocket id — behavior change

Client correlation IDs are now explicitly client-owned instead of always being echoed from a server-generated internal request ID.

What you need to do:

  • On REST, send a valid X-Request-ID header if you rely on requestId / request_id in the response body.
  • On WebSocket, send a valid bounded id (or requestId) if you rely on echoed request IDs in replies.
  • Stop assuming responses always contain a request ID when you did not send one.

Correlation ID changes

SurfaceBeforeAfterNotes
REST top-level requestId / request_idServer-generated internal ID was echoedClient-provided X-Request-ID is echoedOmitted when the header is absent.
transferCollateral.response.requestId / withdrawCollateral.response.requestIdReflected internal request IDReflects client request IDEmpty when the client does not provide one.
WebSocket response id / requestIdServer side request field was echoedOnly client-provided ID is echoedOmitted when the client omits it.
ValidationMinimalClient IDs are validatedInvalid IDs are rejected instead of being silently accepted.

createSubaccount — behavior change

Some creation failures now return structured client/business errors instead of generic internal errors.

What you need to do:

  • Handle 400 responses for tier-limit and validation failures as normal non-retriable API outcomes.
  • If you key business logic off error codes, add handling for MAX_SUB_ACCOUNTS_EXCEEDED and inspect error.details.

Error behavior

  • Before: Tier/precondition failures could surface as generic 500 "Failed to create subaccount".
  • After: Tier/precondition failures can return 400 with structured error data such as code: "MAX_SUB_ACCOUNTS_EXCEEDED" and metadata like currentCount, maxAllowed, and tierName.

cancelOrders — response change

The per-item status mapping now matches the actual cancellation outcome.

What you need to do:

  • Expect successful cancellations under statuses[].canceled.
  • Expect failed cancellations under statuses[].error.
  • Remove any workaround that treated error entries as successful cancels.

Status mapping

FieldBeforeAfterNotes
statuses[].canceledCould be missing on successful cancellationsPresent on successful cancellationsCorrect success mapping.
statuses[].errorCould be populated for successful cancellationsOnly populated on actual failuresCorrect failure mapping.

orderbookUpdate — behavior change

Orderbook subscriptions are now negotiated and default to incremental diff delivery.

What you need to do:

  • Stop subscribing with symbol: "ALL" for orderbook.
  • If you want old-style full snapshots, subscribe with format: "snapshot".
  • If you use the default format: "diff", implement merge logic plus meseq / prevMeseq continuity checks and checksum validation.

Subscription contract

FieldBeforeAfterNotes
params.symbolSpecific symbol or ALLSpecific symbol onlyALL is now rejected for orderbook.
params.formatNot supporteddiff or snapshotDefault is diff.
params.depthNot supported10, 50, 100Default is 50.
params.updateFrequencyMsNot supported50, 100, 250, 500, 1000Default is 250; depth=100 only supports 250/500/1000.
Notification payloadRepeated full-book snapshotsInitial snapshot, then diffs by defaultDiff messages include only changed levels; deletions use quantity: "0".
EnvelopeBasic notificationAdds meseq, prevMeseq, met, checksum, typeRequired to validate diff continuity and integrity.

candleUpdate — response change

New candle subscriptions now receive an immediate salutation frame before normal candle data.

What you need to do:

  • Branch on data.eventType.
  • Ignore or handle the initial salutation payload before parsing candle OHLC fields.

Initial frame

  • Before: First message after subscribe was a normal candle payload.
  • After: The server may first send:
FieldBeforeAfterNotes
data.eventTypeNot present"salutation"Sent immediately after subscribe.
data.messageNot present"hello"Greeting payload.
data.timestampCandle data timestamp onlyGreeting publish timestampSeparate from candle OHLC payload shape.

subAccountUpdate — response change

Account-stream notifications now carry sequencing and recovery metadata.

What you need to do:

  • Accept additive envelope fields such as seq, meseq, met, and provenanceMissing.
  • If you receive needsResync=true, refresh authoritative account state before trusting further incremental updates.
  • If you parse rejected order events, allow data.reason.

Envelope fields

FieldBeforeAfterNotes
seqNot presentPresentMonotonic per subaccount stream.
meseq, metNot presentOptionalAdded on matching-derived order/trade events.
provenanceMissingNot presentOptional booleanTrue when matching-derived provenance is incomplete.
needsResyncNot presentOptional booleanSignals the client to reconcile from authoritative state.
data.reason on rejected ordersNot presentPresentAdditive rejection detail.

Non-breaking changes

  • **getSubAccountIds** — New opt-in includeDelegations=true request param on POST /v1/info. Without it, the response stays a flat owned-ID array. With it, the response becomes { subAccountIds, delegatedSubAccountIds }.
  • **getDelegatedSigners** — Response adds nullable addedBy.
  • **getDelegationsForDelegate** — Accepts optional owningAddress so a session signer can query a parent delegate's delegations after authorization checks.
  • **removeDelegatedSigner** — Response may include additive cascadeRemovedSigners.
  • **createSubaccount**, **getSubAccount**, and **getSubAccounts** — Responses add accountLimits.maxSubAccounts.
  • **placeOrders** — Rejected status rows can include order alongside error, which improves per-item correlation.
  • **marketPriceUpdate** — Subscribe-time cached snapshots are marked with isSnapshot: true; notifications now include per-stream seq, and payloads may include fundingRate and nextFundingTime.
  • **trade** — Public trade notifications add meseq, met, and provenanceMissing.
  • **subscribe acknowledgement** — Successful websocket subscribe responses can include current seq; orderbook subscribe responses also echo negotiated depth, format, and updateFrequencyMs.

March 3, 2026

Changes relevant to 3rd-party integrators for the release of Synthetix on 2026-03-03.


Summary

ActionChange TypeBreaking?Action Required
All ActionsBehavior changePossiblyEnsure request rates stay within IP limits.
All Trade ActionsBehavior changePossiblyEnsure request rates stay within subaccount limits.
getOrderHistoryResponse changeNoNone.

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


All Actions — Behavior change

Global rate limiting has been expanded across all API endpoints.

What you need to do

  • Ensure your integration handles HTTP 429 (Too Many Requests) responses gracefully.
  • Review your request volume to ensure it stays within the new per-IP and per-subaccount token budgets.

Rate Limiting Changes

  • Per-IP Limits: A new per-IP rate limit now applies to all actions (both trade and info endpoints).
  • Expanded Subaccount Limits: The per-subaccount rate limit, which previously only applied to placeOrders, now applies to all trade actions.
  • Token Costs: Different actions now consume different amounts of rate limit tokens. For placeOrders, the token cost scales by the number of orders in the batch.
  • Authentication-First (REST): For REST requests, rate limiting now occurs after authentication, using the verified subAccountId rather than the unauthenticated value from the payload.
  • Global Enforcement: Rate limits are now enforced globally across all API instances using a Redis-backed token bucket, rather than being tracked in-memory per instance.

Non-breaking changes

  • getOrderHistory — Added a new updateTime field (timestamp) to the response items, indicating when the order was last updated.

February 23, 2026

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


Summary

ActionChange TypeBreaking?Action Required
placeOrdersResponse changeNoMigrate from resting.id/filled.id/canceled.id to resting.order/filled.order/canceled.order
placeOrdersBehavior changePossiblyclientOrderId format relaxed — update validation if you enforce the old hex format
cancelOrdersResponse changeNoMigrate from canceled.id to canceled.order
cancelAllOrdersResponse changeNoMigrate from orderId (string) to order (composite)
cancelAllOrdersBehavior changePossiblysymbols now required; use ["*"] to cancel all markets
modifyOrderResponse changeNoMigrate from orderId to order; response is now a typed object
getOpenOrdersResponse changeNoMigrate orderIdorder, takeProfitOrderIdtakeProfitOrder, stopLossOrderIdstopLossOrder
getOrderHistoryResponse changeNoMigrate from orderId to order
getTradesResponse changePossiblyorderIdorder; clientOrderId removed (now in order.clientId)
getPositionsResponse changeNoMigrate takeProfitOrderIdstakeProfitOrders, stopLossOrderIdsstopLossOrders
getBalanceUpdatesResponse changePossiblyid changed from number to string; new TRANSFER filter; new fromSubAccountId field
getTransfersNew endpointNoImplement if needed
transferCollateralBehavior changePossiblyFailed transfers now return structured errors instead of gRPC errors
WebSocket order eventsResponse changeNoMigrate from orderId to order
WebSocket tradeExecuted eventsResponse changeNoMigrate from orderId to order
WebSocket trade notificationsResponse changeNoNew channel and timestamp fields added

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


Composite Order ID — cross-cutting change

Most order-related endpoints and WebSocket events now return a composite order field instead of (or alongside) the plain orderId string. This is the single largest change in this release and affects many actions listed in the summary.

New order field shape

{
  "order": {
    "venueId": "123456789",
    "clientId": "my-order-abc"
  }
}
FieldTypeDescription
venueIdstringSystem-generated order identifier (same value previously in orderId)
clientIdstringClient-provided order ID. Empty string "" if none was set.

Deprecation of orderId

The old orderId field is still present in all responses and contains the venue-only ID (as a string). It is deprecated and will be removed in a future release. Migrate to the order field.

What you need to do

Update your response parsing to read from the order object. You can do this gradually — the deprecated fields continue to work during the transition. The table below lists every deprecated → new field mapping across all affected endpoints.

Deprecated → new field mapping by endpoint

Endpoint / EventOld field(s)New field(s)Notes
placeOrdersresting.id, filled.id, canceled.idresting.order, filled.order, canceled.orderEach is { venueId, clientId }
cancelOrderscanceled.idcanceled.orderSame shape as placeOrders statuses
cancelAllOrdersorderIdorder
modifyOrderorderIdorderResponse is now a typed object with explicit fields
getOpenOrdersorderId, takeProfitOrderId, stopLossOrderIdorder, takeProfitOrder, stopLossOrderTP/SL fields omitted when absent
getOrderHistoryorderIdorder
getTradesorderId, clientOrderIdorderclientOrderId is removed, not deprecated — use order.clientId
getPositionstakeProfitOrderIds (string[]), stopLossOrderIds (string[])takeProfitOrders (object[]), stopLossOrders (object[])Arrays of { venueId, clientId }
WS order eventsorderIdorderAll event types (accepted, filled, cancelled, modified, etc.)
WS tradeExecutedorderIdorder
WS trade notificationschannel, timestampAdditive only — new fields "trade" and server timestamp in ms

All deprecated fields remain present during the transition period unless marked removed above.


placeOrders — response change

The placeOrders response has a nested structure that differs from other endpoints, so a full example is shown here.

Each status in the statuses array (resting, filled, canceled) now contains a composite order field alongside the deprecated id.

Response example

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "resting": {
          "order": { "venueId": "123456789", "clientId": "my-order-1" },
          "id": "123456789"
        }
      },
      {
        "filled": {
          "order": { "venueId": "987654321", "clientId": "" },
          "id": "987654321",
          "totalSize": "0.5",
          "avgPrice": "95000.00"
        }
      }
    ]
  }
}

resting.id, filled.id, and canceled.id are deprecated (still present, contain venue ID only). filled.totalSize and filled.avgPrice are unchanged.


placeOrdersclientOrderId format relaxation

What you need to do

If you validate clientOrderId on your side before sending, update your validation. If you were not using clientOrderId, no action needed.

What changed

  • Before: clientOrderId must be a 32-character hex string with 0x prefix (66 chars total, matching /^0x[a-fA-F0-9]{32}$/)
  • After: clientOrderId can be any string up to 255 characters containing alphanumeric characters and .-/+_=

New validation rules

RuleValue
Max length255 characters
Allowed charactersa-z, A-Z, 0-9, ., -, /, +, _, =
Leading/trailing whitespaceTrimmed automatically
RequiredNo (still optional)

cancelAllOrders — behavior change

What you need to do

If you currently send cancelAllOrders without symbols or with an empty symbols array, you must now provide at least one symbol. To cancel across all markets, send ["*"].

What changed

  • symbols field is now required and must be non-empty. Previously, an empty or missing symbols array was accepted.
  • A new ["*"] wildcard cancels orders across all markets.
  • The wildcard must be the sole element — ["*", "ETH-USDT"] is invalid.

Request examples

Cancel all orders on specific markets (unchanged):

{ "params": { "action": "cancelAllOrders", "symbols": ["BTC-USDT", "ETH-USDT"] } }

Cancel all orders across all markets (new):

{ "params": { "action": "cancelAllOrders", "symbols": ["*"] } }

New error cases

ConditionError
symbols is [] or missing"symbols must be nonempty"
["*", "ETH-USDT"]"symbols[0]: wildcard \"*\" must be the only element in symbols"

getTrades — response change

Migrate from orderId to order. Note that the standalone clientOrderId field has been removed (not just deprecated) — it is now accessible only as order.clientId.

Response example (single trade)

{
  "tradeId": "555",
  "order": { "venueId": "100", "clientId": "my-order-1" },
  "orderId": "100",
  "symbol": "BTC-USDT",
  "side": "buy",
  "price": "95000.00",
  "quantity": "0.1",
  "realizedPnl": "0",
  "fee": "4.75",
  "feeRate": "0.0005",
  "timestamp": 1740307200000,
  "maker": false,
  "reduceOnly": false,
  "markPrice": "95010.00",
  "entryPrice": "95000.00",
  "triggeredByLiquidation": false,
  "direction": "long",
  "postOnly": false
}

getBalanceUpdates — response change

What you need to do

  1. If you parse the id field as a number, switch to parsing it as a string.
  2. If you use actionFilter, note the new TRANSFER filter and the changed error message format for invalid filters.
  3. Optionally handle the new fromSubAccountId field for transfer records.

Field changes

FieldBeforeAfter
idnumberstring
action valuesDEPOSIT, WITHDRAWALDEPOSIT, WITHDRAWAL, TRANSFER
fromSubAccountIdnot presentstring (optional, present for transfers)

actionFilter changes

  • New valid filter value: TRANSFER
  • Error message format change:
    • Before: "actionFilter must be 'DEPOSIT', 'WITHDRAWAL', or comma-separated combination"
    • After: "invalid action filter, available filters: DEPOSIT, WITHDRAWAL, TRANSFER"

Response example (transfer record)

{
  "id": "42",
  "subAccountId": "2011391943438766080",
  "action": "TRANSFER",
  "status": "success",
  "amount": "100",
  "collateral": "USDT",
  "timestamp": 1740307200000,
  "fromSubAccountId": "2011391943438766080",
  "toSubAccountId": "3022502054549877191"
}

getTransfers — new endpoint

Returns transfer history for a subaccount, including collateral transfers between subaccounts. Supports filtering by symbol and time range, with pagination.

What you need to do

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

Request

{
  "params": {
    "action": "getTransfers",
    "symbol": "USDT",
    "limit": 50,
    "offset": 0,
    "startTime": 1740220800000,
    "endTime": 1740307200000
  }
}
FieldTypeRequiredDescription
params.actionstringYesMust be "getTransfers"
params.symbolstringNoFilter by collateral symbol (e.g. "USDT")
params.limitnumberNoMax results. Default 50, max 1000
params.offsetnumberNoPagination offset. Default 0
params.startTimenumberNoStart timestamp in ms
params.endTimenumberNoEnd timestamp in ms. Max range: 30 days

Response

{
  "status": "ok",
  "response": {
    "transfers": [
      {
        "transferId": "12345",
        "from": "2011391943438766080",
        "to": "3022502054549877191",
        "symbol": "USDT",
        "amount": "100",
        "transferType": "COLLATERAL_TRANSFER",
        "status": "success",
        "requestId": "req_abc123",
        "timestamp": 1740307200000
      }
    ],
    "total": 1
  }
}
FieldTypeDescription
transferIdstringTransfer identifier
fromstringSource subaccount ID
tostringDestination subaccount ID
symbolstringCollateral symbol
amountstringTransfer amount
transferTypestringTransfer type (e.g. "COLLATERAL_TRANSFER")
statusstringTransfer status
requestIdstringOriginal request ID
errorMessagestringError message (omitted if empty)
timestampnumberTransfer timestamp in ms
totalnumberTotal number of matching transfers

Errors

HTTP StatusConditionError Code
400Invalid format or missing subaccountIdVALIDATION_ERROR
400Invalid time range (exceeds 30 days)VALIDATION_ERROR
400Negative limit or offset, or limit > 1000VALIDATION_ERROR
500Internal service failureINTERNAL_ERROR

transferCollateral — behavior change

What you need to do

If you rely on error responses from failed transfers, update your error handling. Failed transfers now return structured error responses with specific error codes instead of opaque gRPC errors. Request and response schemas for successful transfers are unchanged.

What changed

Previously, transfer failures (e.g. insufficient balance) were returned as gRPC-level errors, often resulting in generic 500 responses. Now, failed transfers return structured JSON error responses with appropriate HTTP status codes:

Error conditionHTTP StatusError Code
Asset not found400ASSET_NOT_FOUND
Insufficient margin400INSUFFICIENT_MARGIN
Invalid value400INVALID_VALUE
Validation error400VALIDATION_ERROR
Internal failure500INTERNAL_ERROR

February 6, 2026

API Changelog: release-all-20260205 & release-all-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.

January 30, 2026

WebSocket Notifications: New channel Field Replaces method

WebSocket notification messages now use a channel field instead of method to identify the notification type. This change provides naming consistency with the rest of the API (which uses camelCase).

What Changed

All WebSocket notification messages now include:

  • channel (new): Identifies the notification type using camelCase naming
  • timestamp (new): Unix timestamp in milliseconds when the message was sent

Channel Names

Old method ValueNew channel Value
candle_updatecandleUpdate
market_price_updatemarketPriceUpdate
orderbook_depth_updateorderbookUpdate
subAccountEventsubAccountUpdate

New Message Format

{
  "channel": "candleUpdate",
  "data": { ... },
  "timestamp": 1706659200000
}

Migration

Update your WebSocket message handlers to check the channel field:

Before:
if (message.method === "candle_update") {
  handleCandleUpdate(message.data);
}
After:
if (message.channel === "candleUpdate") {
  handleCandleUpdate(message.data);
}

Affected Subscriptions

Notes

  • Subscription requests still use "method": "subscribe" - only notification messages have changed
  • The method field has been removed from notification messages
  • The new timestamp field provides the server-side timestamp for when the notification was sent

January 6, 2026

Breaking Change: Nonce Removed from SubAccountAction Requests

SubAccountAction endpoints no longer require the nonce parameter in requests or EIP-712 signatures.

Affected Endpoints

  • getPositions
  • getOpenOrders
  • getOrdersHistory
  • getTrades
  • getFundingPayments
  • getSubAccount
  • getDelegatedSigners
  • getBalanceUpdates

What Changed

  • EIP-712 Type: SubAccountAction no longer includes nonce field
  • Request Body: nonce parameter removed from request structure
  • Signature: Sign only subAccountId, action, and optional expiresAfter

New SubAccountAction EIP-712 Type

SubAccountAction: [
  { name: "subAccountId", type: "uint256" },
  { name: "action", type: "string" },
  { name: "expiresAfter", type: "uint256" }
]

Migration

Before:
{
  "params": { "action": "getPositions", "subAccountId": "123456789" },
  "nonce": 1735689600000,
  "signature": { "v": 28, "r": "0x...", "s": "0x..." }
}
After:
{
  "params": { "action": "getPositions", "subAccountId": "123456789" },
  "signature": { "v": 28, "r": "0x...", "s": "0x..." }
}

Notes

  • The expiresAfter parameter remains optional for request expiration
  • Trading endpoints (placeOrders, cancelOrders, modifyOrder) still require nonce
  • Update your EIP-712 signing code to remove nonce from the message for SubAccountAction endpoints

December 31, 2025

Fixed: EIP-712 Signature Documentation for placeOrders

Corrected EIP-712 type definitions in all placeOrders documentation to match backend implementation.

What Changed

  • Getting Started Guide: Fixed Python and JavaScript examples to include complete EIP-712 types
  • WebSocket placeOrders: Corrected type name from NewOrdersRequest to PlaceOrders, fixed subAccountId type from uint64 to uint256, added missing closePosition and postOnly fields to all usage examples
  • Code Examples: Applied ES6 object shorthand syntax for cleaner, modern JavaScript

EIP-712 Types Fixed

The PlaceOrders type now correctly includes all required fields:

PlaceOrders: [
  { name: "subAccountId", type: "uint256" },
  { name: "orders", type: "Order[]" },
  { name: "grouping", type: "string" },
  { name: "nonce", type: "uint256" },
  { name: "expiresAfter", type: "uint256" }
]
 
Order: [
  { name: "symbol", type: "string" },
  { name: "side", type: "string" },
  { name: "orderType", type: "string" },
  { name: "price", type: "string" },
  { name: "triggerPrice", type: "string" },
  { name: "quantity", type: "string" },
  { name: "reduceOnly", type: "bool" },
  { name: "isTriggerMarket", type: "bool" },
  { name: "clientOrderId", type: "string" },
  { name: "closePosition", type: "bool" }
]

Previously missing fields: subAccountId, grouping, clientOrderId, closePosition

Impact

This fixes signature validation failures that were preventing market maker integrations from placing orders successfully.


December 14, 2025

Breaking Change: getSubAccounts → getSubAccount

The getSubAccounts endpoint has been replaced with getSubAccount for retrieving subaccount information.

What Changed

  • Endpoint: getSubAccountsgetSubAccount
  • Request: Now requires params.subAccountId parameter
  • Response: Returns single subaccount object instead of array
  • EIP-712 Signature: Uses SubAccountAction type with fields: subAccountId, action, expiresAfter (nonce removed as of January 6, 2026)

Migration

// Before
{ "params": { "action": "getSubAccounts" } }
// Returns: array of all subaccounts
 
// After
{ "params": { "action": "getSubAccount", "subAccountId": "123456789" } }
// Returns: single subaccount object

December 1, 2025

  • Begin tracking changes
  • Only implemented methods shown