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
| Action | Change Type | Breaking? | Action Required |
|---|---|---|---|
WebSocket id / requestId | Behavior change | Possibly | Ensure id and requestId are JSON strings, not numbers |
clientOrderId | Behavior change | Yes | Remove leading/trailing whitespace from client order IDs |
triggerPriceType | Behavior change | Possibly | Use exactly "mark" or "last" (case-sensitive, no trim) |
| Request body size | Behavior change | Possibly | Keep request bodies ≤ 20 KB or handle HTTP 413 |
createSubaccount name | Behavior change | Possibly | Reject names with leading/trailing whitespace |
| Time params conflict | Behavior change | Possibly | Do not send both startTime and fromTime (or endTime and toTime) |
| Rate limits | Behavior change | Possibly | Expect updated limits; see deployment config |
cancelOrders by cloid | New capability | No | Optional: use clientOrderIds instead of venueOrderIds |
modifyOrder by cloid | New capability | No | Optional: use clientOrderId instead of venueOrderId |
getPositionHistory | New endpoint | No | Query closed position history |
getCollaterals (info) | New endpoint | No | Fetch collateral configs from info API |
getDelegationsForDelegate | Response change | No | New accountValue field per delegation |
| Order rejections | Response change | No | New errorCode field for programmatic handling |
| Collateral tiers | Response change | No | New valueAddition, valueRatio in tier objects |
| Candle timeframes | New capability | No | 30m, 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
idandrequestIdin 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 numericid/requestId.
clientOrderId — Behavior change
Leading and trailing whitespace in clientOrderId is now rejected instead of trimmed.
What you need to do
- Strip whitespace from
clientOrderIdbefore 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
startTimeandendTime.fromTimeandtoTimeremain supported but are deprecated. - Do not send conflicting pairs (e.g.
startTimeandfromTimewith different values). This returns HTTP 400.
Non-breaking changes
**cancelOrders** — Can cancel byclientOrderIdsinstead ofvenueOrderIds. Same action, new optional param shape.**modifyOrder**— Can modify byclientOrderIdinstead ofvenueOrderId. Same action, new optional param shape.**getPositionHistory**— New endpoint. Query closed position history withsymbol,startTime,endTime,limit,offset.**getCollaterals**— New info endpoint. Returns collateral configs with tiers. Replaces subaccount-specific collateral fetch.**getDelegationsForDelegate**— NewaccountValuefield (string, USDT) per delegated account.- Order rejections — New
errorCodefield for place/cancel/modify responses. See Error Handling for codes and retry policy. - Collateral tiers — New
valueAdditionandvalueRatiofields 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 tofalse) 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 addressdelegatedSubAccountIds— 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.
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
subAccountIdandnameparameters with EIP-712 signature - Response: Returns the updated
subAccountIdandname - Authentication: Subaccount-level EIP-712 signing with
UpdateSubAccountNametyped 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 haircutadjustedCollateralValue: collateral value after applying the haircut discount
This applies to both the REST and WebSocket variants of getSubAccount, as well as getSubAccounts.
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.
11 March 2026
Changes relevant to 3rd-party integrators for the release of Synthetix on 2026-03-11.
Summary
| Action | Change Type | Breaking? | Action Required |
|---|---|---|---|
addDelegatedSigner | Behavior change | Yes | Send exactly one permission value and update delegation-management flows for real delegate vs session semantics. |
removeDelegatedSigner | Behavior change | Yes | Expect hierarchical removal rules and owner-side cascade deletion of child session signers. |
All WebSocket channels excl. orderbookUpdate | Behavior change | Possibly | New seq field added to WebSocket channels: update parsers to accept new fields; implement gap detection using seq. |
requestId / X-Request-ID / WebSocket id | Behavior change | Possibly | Send an explicit valid client request ID if you rely on echoed correlation IDs; stop assuming the server always generates one in the payload. |
createSubaccount | Behavior change | Possibly | Treat tier-limit and validation failures as structured 400 errors, not generic retriable 500s. |
cancelOrders | Response change | Possibly | Remove any client workaround that interpreted error items as successful cancellations. |
cancelAllOrders | Response change | Possibly | Update response parsing of the message field, which is now error-only; success returns empty/null. |
orderbookUpdate | Behavior change | Yes | Orderbook 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. |
candleUpdate | Response change | Possibly | Handle an initial salutation payload before candle OHLC updates. |
subAccountUpdate | Response change | Possibly | Tolerate seq/provenance fields and resync from authoritative state when needsResync=true. |
getSubAccountIds | Response change | No | No change unless you opt into includeDelegations=true; then parse an object instead of a flat array. |
getDelegatedSigners / getDelegationsForDelegate | Response change | No | Allow additive delegation fields such as addedBy, cascadeRemovedSigners, and optional owningAddress. |
createSubaccount / getSubAccount / getSubAccounts | Response change | No | Allow the additive accountLimits.maxSubAccounts field. |
placeOrders | Response change | No | If you decode status rows strictly, allow order on rejected status items. |
marketPriceUpdate / trade | Response change | No | Allow 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
delegatefor manager-style signers andsessionfor trading/session keys. - Do not rely on delegates or session signers being able to create arbitrary downstream delegations.
Permission changes
| Field | Before | After | Notes |
|---|---|---|---|
permissions | Any non-empty array was broadly tolerated in practice | Exactly one value is required | Multi-value arrays are rejected. |
permissions[0] | Semantically loose | delegate, session, or legacy trading only | trading remains accepted for compatibility but is normalized to session. |
| Delegation authority | Loose / flattened behavior | Hierarchical | Owner 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
cascadeRemovedSignerslist.
Removal changes
| Field | Before | After | Notes |
|---|---|---|---|
| Removal authority | Looser behavior | Hierarchical | Delegate can remove only session signers it created; session signer cannot remove delegations. |
| Response | subAccountId, walletAddress | Adds optional cascadeRemovedSigners | Returned 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-IDheader if you rely onrequestId/request_idin the response body. - On WebSocket, send a valid bounded
id(orrequestId) 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
| Surface | Before | After | Notes |
|---|---|---|---|
REST top-level requestId / request_id | Server-generated internal ID was echoed | Client-provided X-Request-ID is echoed | Omitted when the header is absent. |
transferCollateral.response.requestId / withdrawCollateral.response.requestId | Reflected internal request ID | Reflects client request ID | Empty when the client does not provide one. |
WebSocket response id / requestId | Server side request field was echoed | Only client-provided ID is echoed | Omitted when the client omits it. |
| Validation | Minimal | Client IDs are validated | Invalid 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
400responses 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_EXCEEDEDand inspecterror.details.
Error behavior
- Before: Tier/precondition failures could surface as generic
500"Failed to create subaccount". - After: Tier/precondition failures can return
400with structured error data such ascode: "MAX_SUB_ACCOUNTS_EXCEEDED"and metadata likecurrentCount,maxAllowed, andtierName.
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
errorentries as successful cancels.
Status mapping
| Field | Before | After | Notes |
|---|---|---|---|
statuses[].canceled | Could be missing on successful cancellations | Present on successful cancellations | Correct success mapping. |
statuses[].error | Could be populated for successful cancellations | Only populated on actual failures | Correct 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"fororderbook. - If you want old-style full snapshots, subscribe with
format: "snapshot". - If you use the default
format: "diff", implement merge logic plusmeseq/prevMeseqcontinuity checks andchecksumvalidation.
Subscription contract
| Field | Before | After | Notes |
|---|---|---|---|
params.symbol | Specific symbol or ALL | Specific symbol only | ALL is now rejected for orderbook. |
params.format | Not supported | diff or snapshot | Default is diff. |
params.depth | Not supported | 10, 50, 100 | Default is 50. |
params.updateFrequencyMs | Not supported | 50, 100, 250, 500, 1000 | Default is 250; depth=100 only supports 250/500/1000. |
| Notification payload | Repeated full-book snapshots | Initial snapshot, then diffs by default | Diff messages include only changed levels; deletions use quantity: "0". |
| Envelope | Basic notification | Adds meseq, prevMeseq, met, checksum, type | Required 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:
| Field | Before | After | Notes |
|---|---|---|---|
data.eventType | Not present | "salutation" | Sent immediately after subscribe. |
data.message | Not present | "hello" | Greeting payload. |
data.timestamp | Candle data timestamp only | Greeting publish timestamp | Separate 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, andprovenanceMissing. - 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
| Field | Before | After | Notes |
|---|---|---|---|
seq | Not present | Present | Monotonic per subaccount stream. |
meseq, met | Not present | Optional | Added on matching-derived order/trade events. |
provenanceMissing | Not present | Optional boolean | True when matching-derived provenance is incomplete. |
needsResync | Not present | Optional boolean | Signals the client to reconcile from authoritative state. |
data.reason on rejected orders | Not present | Present | Additive rejection detail. |
Non-breaking changes
**getSubAccountIds** — New opt-inincludeDelegations=truerequest param onPOST /v1/info. Without it, the response stays a flat owned-ID array. With it, the response becomes{ subAccountIds, delegatedSubAccountIds }.**getDelegatedSigners** — Response adds nullableaddedBy.**getDelegationsForDelegate**— Accepts optionalowningAddressso a session signer can query a parent delegate's delegations after authorization checks.**removeDelegatedSigner**— Response may include additivecascadeRemovedSigners.**createSubaccount**,**getSubAccount**, and**getSubAccounts**— Responses addaccountLimits.maxSubAccounts.**placeOrders**— Rejected status rows can includeorderalongsideerror, which improves per-item correlation.**marketPriceUpdate**— Subscribe-time cached snapshots are marked withisSnapshot: true; notifications now include per-streamseq, and payloads may includefundingRateandnextFundingTime.**trade**— Public trade notifications addmeseq,met, andprovenanceMissing.**subscribeacknowledgement** — Successful websocket subscribe responses can include currentseq;orderbooksubscribe responses also echo negotiateddepth,format, andupdateFrequencyMs.
March 3, 2026
Changes relevant to 3rd-party integrators for the release of Synthetix on 2026-03-03.
Summary
| Action | Change Type | Breaking? | Action Required |
|---|---|---|---|
All Actions | Behavior change | Possibly | Ensure request rates stay within IP limits. |
All Trade Actions | Behavior change | Possibly | Ensure request rates stay within subaccount limits. |
getOrderHistory | Response change | No | None. |
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
subAccountIdrather 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 newupdateTimefield (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
| Action | Change Type | Breaking? | Action Required |
|---|---|---|---|
placeOrders | Response change | No | Migrate from resting.id/filled.id/canceled.id to resting.order/filled.order/canceled.order |
placeOrders | Behavior change | Possibly | clientOrderId format relaxed — update validation if you enforce the old hex format |
cancelOrders | Response change | No | Migrate from canceled.id to canceled.order |
cancelAllOrders | Response change | No | Migrate from orderId (string) to order (composite) |
cancelAllOrders | Behavior change | Possibly | symbols now required; use ["*"] to cancel all markets |
modifyOrder | Response change | No | Migrate from orderId to order; response is now a typed object |
getOpenOrders | Response change | No | Migrate orderId → order, takeProfitOrderId → takeProfitOrder, stopLossOrderId → stopLossOrder |
getOrderHistory | Response change | No | Migrate from orderId to order |
getTrades | Response change | Possibly | orderId → order; clientOrderId removed (now in order.clientId) |
getPositions | Response change | No | Migrate takeProfitOrderIds → takeProfitOrders, stopLossOrderIds → stopLossOrders |
getBalanceUpdates | Response change | Possibly | id changed from number to string; new TRANSFER filter; new fromSubAccountId field |
getTransfers | New endpoint | No | Implement if needed |
transferCollateral | Behavior change | Possibly | Failed transfers now return structured errors instead of gRPC errors |
WebSocket order events | Response change | No | Migrate from orderId to order |
WebSocket tradeExecuted events | Response change | No | Migrate from orderId to order |
WebSocket trade notifications | Response change | No | New 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"
}
}| Field | Type | Description |
|---|---|---|
venueId | string | System-generated order identifier (same value previously in orderId) |
clientId | string | Client-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 / Event | Old field(s) | New field(s) | Notes |
|---|---|---|---|
placeOrders | resting.id, filled.id, canceled.id | resting.order, filled.order, canceled.order | Each is { venueId, clientId } |
cancelOrders | canceled.id | canceled.order | Same shape as placeOrders statuses |
cancelAllOrders | orderId | order | |
modifyOrder | orderId | order | Response is now a typed object with explicit fields |
getOpenOrders | orderId, takeProfitOrderId, stopLossOrderId | order, takeProfitOrder, stopLossOrder | TP/SL fields omitted when absent |
getOrderHistory | orderId | order | |
getTrades | orderId, clientOrderId | order | clientOrderId is removed, not deprecated — use order.clientId |
getPositions | takeProfitOrderIds (string[]), stopLossOrderIds (string[]) | takeProfitOrders (object[]), stopLossOrders (object[]) | Arrays of { venueId, clientId } |
WS order events | orderId | order | All event types (accepted, filled, cancelled, modified, etc.) |
WS tradeExecuted | orderId | order | |
WS trade notifications | — | channel, timestamp | Additive 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.
placeOrders — clientOrderId 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:
clientOrderIdmust be a 32-character hex string with0xprefix (66 chars total, matching/^0x[a-fA-F0-9]{32}$/) - After:
clientOrderIdcan be any string up to 255 characters containing alphanumeric characters and.-/+_=
New validation rules
| Rule | Value |
|---|---|
| Max length | 255 characters |
| Allowed characters | a-z, A-Z, 0-9, ., -, /, +, _, = |
| Leading/trailing whitespace | Trimmed automatically |
| Required | No (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
symbolsfield is now required and must be non-empty. Previously, an empty or missingsymbolsarray 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
| Condition | Error |
|---|---|
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
- If you parse the
idfield as a number, switch to parsing it as a string. - If you use
actionFilter, note the newTRANSFERfilter and the changed error message format for invalid filters. - Optionally handle the new
fromSubAccountIdfield for transfer records.
Field changes
| Field | Before | After |
|---|---|---|
id | number | string |
action values | DEPOSIT, WITHDRAWAL | DEPOSIT, WITHDRAWAL, TRANSFER |
fromSubAccountId | not present | string (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"
- Before:
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
}
}| Field | Type | Required | Description |
|---|---|---|---|
params.action | string | Yes | Must be "getTransfers" |
params.symbol | string | No | Filter by collateral symbol (e.g. "USDT") |
params.limit | number | No | Max results. Default 50, max 1000 |
params.offset | number | No | Pagination offset. Default 0 |
params.startTime | number | No | Start timestamp in ms |
params.endTime | number | No | End 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
}
}| Field | Type | Description |
|---|---|---|
transferId | string | Transfer identifier |
from | string | Source subaccount ID |
to | string | Destination subaccount ID |
symbol | string | Collateral symbol |
amount | string | Transfer amount |
transferType | string | Transfer type (e.g. "COLLATERAL_TRANSFER") |
status | string | Transfer status |
requestId | string | Original request ID |
errorMessage | string | Error message (omitted if empty) |
timestamp | number | Transfer timestamp in ms |
total | number | Total number of matching transfers |
Errors
| HTTP Status | Condition | Error Code |
|---|---|---|
400 | Invalid format or missing subaccountId | VALIDATION_ERROR |
400 | Invalid time range (exceeds 30 days) | VALIDATION_ERROR |
400 | Negative limit or offset, or limit > 1000 | VALIDATION_ERROR |
500 | Internal service failure | INTERNAL_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 condition | HTTP Status | Error Code |
|---|---|---|
| Asset not found | 400 | ASSET_NOT_FOUND |
| Insufficient margin | 400 | INSUFFICIENT_MARGIN |
| Invalid value | 400 | INVALID_VALUE |
| Validation error | 400 | VALIDATION_ERROR |
| Internal failure | 500 | INTERNAL_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
| Action | Change Type | Breaking? | Action Required |
|---|---|---|---|
getSubAccounts | New endpoint | No | Implement if needed |
removeAllDelegatedSigners | New endpoint | No | Implement if needed; requires new EIP-712 type |
withdrawCollateral | Behavior change | Possibly | Update error message parsing if you match on fee errors |
getTrades, placeOrders | Internal only | No | None. 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.
getSubAccount):
| Field | Type | Description |
|---|---|---|
feeRates | FeeRateInfo | Fee tier for this subaccount |
delegatedSigners | DelegatedSigner[] | All delegates authorized on this subaccount. Empty [] if none exist. |
FeeRateInfo fields:
| Field | Type | Example | Description |
|---|---|---|---|
makerFeeRate | string | "0.0002" | Maker fee rate (decimal) |
takerFeeRate | string | "0.0005" | Taker fee rate (decimal) |
tierName | string | "Regular User" | Fee tier name |
DelegatedSigner fields:
| Field | Type | Example | Description |
|---|---|---|---|
subAccountId | string | "2011391943438766080" | Subaccount this delegation applies to |
walletAddress | string | "0xDelegateWalletAddress" | Delegate's Ethereum address |
permissions | string[] | ["trading"] | Granted permissions |
expiresAt | number | null | 1738800000000 | Expiry as unix ms, or null for no expiration |
Errors
| HTTP Status | Condition | Error Code |
|---|---|---|
400 | Missing subaccountId | VALIDATION_ERROR |
500 | Internal service failure | INTERNAL_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.
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
}| Field | Type | Required | Description |
|---|---|---|---|
params.action | string | Yes | Must be "removeAllDelegatedSigners" |
subaccountId | string | Yes | Subaccount to remove all delegates from |
nonce | number | Yes | Timestamp in ms (replay protection) |
signature | object | Yes | EIP-712 signature { v, r, s } |
expiresAfter | number | No | Request expiry as unix ms. 0 = no expiry |
Response
{
"status": "ok",
"response": {
"subAccountId": "123",
"removedSigners": ["0xDelegateAddr1", "0xDelegateAddr2"]
}
}| Field | Type | Description |
|---|---|---|
subAccountId | string | The subaccount from which signers were removed |
removedSigners | string[] | Wallet addresses of removed delegates. Empty [] if none |
Errors
| HTTP Status | Condition | Error Code |
|---|---|---|
400 | Invalid format or validation failure | VALIDATION_ERROR |
403 | Caller is a delegate, not the owner | VALIDATION_ERROR |
404 | Subaccount not found | NOT_FOUND |
500 | Internal service failure | INTERNAL_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.
| Asset | Fee | Approximate USD equivalent |
|---|---|---|
USDT | 5 | $5 |
WETH | 0.0017 | ~$5 at ~$3,000/ETH |
BTC | 0.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 namingtimestamp(new): Unix timestamp in milliseconds when the message was sent
Channel Names
Old method Value | New channel Value |
|---|---|
candle_update | candleUpdate |
market_price_update | marketPriceUpdate |
orderbook_depth_update | orderbookUpdate |
subAccountEvent | subAccountUpdate |
New Message Format
{
"channel": "candleUpdate",
"data": { ... },
"timestamp": 1706659200000
}Migration
Update your WebSocket message handlers to check the channel field:
if (message.method === "candle_update") {
handleCandleUpdate(message.data);
}if (message.channel === "candleUpdate") {
handleCandleUpdate(message.data);
}Affected Subscriptions
Notes
- Subscription requests still use
"method": "subscribe"- only notification messages have changed - The
methodfield has been removed from notification messages - The new
timestampfield 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
getPositionsgetOpenOrdersgetOrdersHistorygetTradesgetFundingPaymentsgetSubAccountgetDelegatedSignersgetBalanceUpdates
What Changed
- EIP-712 Type:
SubAccountActionno longer includesnoncefield - Request Body:
nonceparameter removed from request structure - Signature: Sign only
subAccountId,action, and optionalexpiresAfter
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..." }
}{
"params": { "action": "getPositions", "subAccountId": "123456789" },
"signature": { "v": 28, "r": "0x...", "s": "0x..." }
}Notes
- The
expiresAfterparameter remains optional for request expiration - Trading endpoints (
placeOrders,cancelOrders,modifyOrder) still requirenonce - Update your EIP-712 signing code to remove
noncefrom 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
NewOrdersRequesttoPlaceOrders, fixedsubAccountIdtype fromuint64touint256, added missingclosePositionandpostOnlyfields 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:
getSubAccounts→getSubAccount - Request: Now requires
params.subAccountIdparameter - Response: Returns single subaccount object instead of array
- EIP-712 Signature: Uses
SubAccountActiontype 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 objectDecember 1, 2025
- Begin tracking changes
- Only implemented methods shown