Get Subaccounts (WebSocket)
Retrieve all subaccounts under the same master account as the authenticated subaccount through the WebSocket connection. Works for both account owners and delegated signers — any subaccount that can authenticate will receive the full list of sibling subaccounts, each including collateral balances, cross margin summary, positions, market preferences, fee rates, and delegated signers. This is a more comprehensive alternative to getSubAccount that returns multiple subaccounts with delegation information included.
Endpoint
ws.send() wss://api.synthetix.io/v1/ws/tradeRequest
Request Format
{
"id": "subaccounts-1",
"method": "post",
"params": {
"action": "getSubAccounts",
"subAccountId": "1867542890123456789",
"expiresAfter": 1704067500000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}
}Parameters
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Client-generated unique request identifier |
method | string | Yes | Must be "post" |
params | object | Yes | Contains all parameters for the request |
Params Object
| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Must be "getSubAccounts" |
subAccountId | string | Yes | The ID of a subaccount owned by the authenticated wallet |
expiresAfter | integer | No | Optional expiration timestamp (milliseconds) |
signature | object | Yes | EIP-712 signature using SubAccountAction type |
- This endpoint uses
SubAccountActionEIP-712 type (no nonce required, onlyexpiresAfteris optional) - Returns all subaccounts associated with the authenticated wallet
Response Format
Success Response
{
"id": "subaccounts-1",
"status": 200,
"result": {
"subAccounts": [
{
"subAccountId": "1867542890123456789",
"masterAccountId": "1867542890123456788",
"subAccountName": "Trading Account 1",
"collaterals": [
{
"symbol": "USDC",
"quantity": "1000.00000000",
"withdrawable": "1000.00000000",
"pendingWithdraw": "0.00000000",
"adjustedCollateralValue": "1000.00",
"collateralValue": "1000.00",
"haircutRate": "0",
"haircutAdjustment": "0.00",
"price": "1.0000",
"calculatedAt": 0
},
{
"symbol": "ETH",
"quantity": "0.5000",
"withdrawable": "0.5000",
"pendingWithdraw": "0.0000",
"adjustedCollateralValue": "975.00",
"collateralValue": "1000.00",
"haircutRate": "0.025",
"haircutAdjustment": "25.00",
"price": "2000.00",
"calculatedAt": 1735689600000
}
],
"crossMarginSummary": {
"accountValue": "6750.50",
"availableMargin": "2415.00",
"totalUnrealizedPnl": "75.00",
"maintenanceMargin": "1207.50",
"initialMargin": "2415.00",
"withdrawable": "4335.50",
"adjustedAccountValue": "6750.50",
"debt": "0.00"
},
"positions": [],
"marketPreferences": {
"leverages": {
"BTC-USDT": 20,
"ETH-USDT": 10
}
},
"feeRates": {
"makerFeeRate": "0.0002",
"takerFeeRate": "0.0005",
"tierName": "Regular User"
},
"accountLimits": {
"maxBorrowCapacity": "10000.00",
"maxOrdersPerMarket": 10,
"maxSubAccounts": 10,
"maxTotalOrders": 100
},
"delegatedSigners": [
{
"subAccountId": "1867542890123456789",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["trading"],
"expiresAt": null,
"addedBy": "0x1234567890abcdef1234567890abcdef12345678"
}
]
},
{
"subAccountId": "1867542890123456790",
"masterAccountId": "1867542890123456788",
"subAccountName": "Trading Account 2",
"collaterals": [
{
"symbol": "USDC",
"quantity": "5000.00000000",
"withdrawable": "5000.00000000",
"pendingWithdraw": "0.00000000",
"adjustedCollateralValue": "5000.00",
"collateralValue": "5000.00",
"haircutRate": "0",
"haircutAdjustment": "0.00",
"price": "1.0000",
"calculatedAt": 0
}
],
"crossMarginSummary": {
"accountValue": "5000.00",
"availableMargin": "5000.00",
"totalUnrealizedPnl": "0.00",
"maintenanceMargin": "0.00",
"initialMargin": "0.00",
"withdrawable": "5000.00",
"adjustedAccountValue": "5000.00",
"debt": "0.00"
},
"positions": [],
"marketPreferences": {
"leverages": {}
},
"feeRates": {
"makerFeeRate": "0.0002",
"takerFeeRate": "0.0005",
"tierName": "Regular User"
},
"accountLimits": {
"maxBorrowCapacity": "10000.00",
"maxOrdersPerMarket": 10,
"maxSubAccounts": 10,
"maxTotalOrders": 100
},
"delegatedSigners": []
}
]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
subAccounts | array | Array of subaccount objects |
Each subaccount in the subAccounts array includes:
| Field | Type | Description |
|---|---|---|
subAccountId | string | Unique subaccount identifier |
masterAccountId | string | null | ID of the master account (null if this is a master account) |
subAccountName | string | Human-readable subaccount name |
collaterals | array | Array of collateral assets held in this subaccount |
crossMarginSummary | object | Cross margin summary with account value, margins, and P&L |
positions | array | Array of open positions for this subaccount |
marketPreferences | object | Market-specific preferences and settings |
marketPreferences.leverages | object | Per-market leverage settings (key: symbol, value: leverage multiplier) |
feeRates | object | Fee rate information for this subaccount |
feeRates.makerFeeRate | string | Maker fee rate as decimal (e.g., "0.0002" for 0.02%) |
feeRates.takerFeeRate | string | Taker fee rate as decimal (e.g., "0.0005" for 0.05%) |
feeRates.tierName | string | Fee tier name (e.g., "Regular User", "Tier 1") |
accountLimits | object | Account limits for this subaccount |
accountLimits.maxBorrowCapacity | string | Maximum borrow capacity in USD |
accountLimits.maxOrdersPerMarket | integer | Maximum number of open orders per market |
accountLimits.maxSubAccounts | integer | Maximum number of subaccounts allowed for the master account |
accountLimits.maxTotalOrders | integer | Maximum total number of open orders across all markets |
| Field | Type | Description |
|---|---|---|
delegatedSigners | array | Array of delegated signer objects for this subaccount |
Collateral Object
| Field | Type | Description |
|---|---|---|
symbol | string | Collateral token symbol (e.g., "USDT") |
quantity | string | Amount of collateral held |
withdrawable | string | Amount available for withdrawal |
pendingWithdraw | string | Amount pending withdrawal |
adjustedCollateralValue | string | Collateral value after applying haircut discount (USD equivalent) |
collateralValue | string | Full collateral value before haircut (USD equivalent) |
haircutRate | string | Haircut rate applied to this collateral (e.g., "0.1" = 10% discount) |
haircutAdjustment | string | Absolute haircut adjustment amount in USD |
price | string | Collateral token price in USD at time of calculation |
calculatedAt | integer | Unix timestamp (milliseconds) when price was last calculated; 0 for USDT |
Cross Margin Summary Object
| Field | Type | Description |
|---|---|---|
accountValue | string | Total account value including PnL |
availableMargin | string | USDT amount available for new positions |
totalUnrealizedPnl | string | Sum of all unrealized PnL |
maintenanceMargin | string | Minimum margin required for all positions |
initialMargin | string | Initial margin required for all positions |
withdrawable | string | USDT amount available for withdrawal |
adjustedAccountValue | string | Adjusted account value for margin calculations |
debt | string | Outstanding debt in the account |
Delegate Object
Delegate Object
The delegate object represents a delegated signer in API responses. Note that the response uses walletAddress while EIP-712 signing uses delegateAddress.
| Field | Type | Description |
|---|---|---|
subAccountId | string | Subaccount ID this delegation applies to |
walletAddress | string | Ethereum wallet address of the delegated signer (42-character hex format) |
permissions | string[] | Array of permission levels granted, such as ["session"] or ["delegate"] |
expiresAt | integer | null | Unix timestamp (milliseconds) when the delegation expires. null indicates no expiration |
addedBy | string | omitted | Wallet address that created this delegation. Omitted for pre-migration records where the creator was not recorded |
Example Delegate Object
{
"subAccountId": "1867542890123456789",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["session"],
"expiresAt": null,
"addedBy": "0x1111111111111111111111111111111111111111"
}Example with Expiration
{
"subAccountId": "1867542890123456789",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["delegate"],
"expiresAt": 1767225600000
}Error Response
{
"id": "subaccounts-1",
"status": 400,
"result": null,
"error": {
"code": 400,
"message": "subaccountId is required"
}
}Implementation Example
class SubaccountsQuery {
constructor(ws, signer) {
this.ws = ws;
this.signer = signer;
this.pendingRequests = new Map();
}
async getSubAccounts(subAccountId) {
const expiresAfter = Date.now() + 300000; // 5 minutes in milliseconds
// Sign using SubAccountAction type (no nonce required)
const signature = await this.signSubAccountAction(subAccountId, "getSubAccounts", expiresAfter);
const request = {
id: `subaccounts-${Date.now()}`,
method: "post",
params: {
action: "getSubAccounts",
subAccountId,
expiresAfter,
signature
}
};
return this.sendRequest(request);
}
async signSubAccountAction(subAccountId, action, expiresAfter = 0) {
const domain = {
name: "Synthetix",
version: "1",
chainId: 1,
verifyingContract: "0x0000000000000000000000000000000000000000"
};
const types = {
SubAccountAction: [
{ name: "subAccountId", type: "uint256" },
{ name: "action", type: "string" },
{ name: "expiresAfter", type: "uint256" }
]
};
const message = {
subAccountId: BigInt(subAccountId),
action,
expiresAfter: BigInt(expiresAfter)
};
const sig = await this.signer.signTypedData(domain, types, message);
const { v, r, s } = ethers.Signature.from(sig);
return { v, r, s };
}
sendRequest(request) {
return new Promise((resolve, reject) => {
this.pendingRequests.set(request.id, { resolve, reject });
this.ws.send(JSON.stringify(request));
setTimeout(() => {
if (this.pendingRequests.has(request.id)) {
this.pendingRequests.delete(request.id);
reject(new Error('Request timeout'));
}
}, 10000);
});
}
handleResponse(response) {
const pending = this.pendingRequests.get(response.id);
if (pending) {
this.pendingRequests.delete(response.id);
if (response.error) {
pending.reject(new Error(response.error.message));
} else {
pending.resolve(response.result);
}
}
}
}
// Usage
const query = new SubaccountsQuery(ws, signer);
const subaccounts = await query.getSubAccounts("1867542890123456789");
subaccounts.subAccounts.forEach(account => {
console.log(`Account: ${account.subAccountName}`);
console.log(`Value: ${account.crossMarginSummary.accountValue}`);
console.log(`Delegates: ${account.delegatedSigners.length}`);
});Code Examples
Get All Subaccounts
{
"id": "get-subaccounts",
"method": "post",
"params": {
"action": "getSubAccounts",
"subAccountId": "1867542890123456789",
"expiresAfter": 0,
"signature": { "v": 28, "r": "0x...", "s": "0x..." }
}
}Implementation Notes
- This endpoint uses
SubAccountActionEIP-712 type (no nonce required) - Returns all subaccounts under the same master account as the authenticated subaccount
- Works for both account owners and delegated signers
- Includes delegated signers for each subaccount, unlike
getSubAccount
| Error Code | Description | Retryable |
|---|---|---|
UNAUTHORIZED | EIP-712 signature validation failed | No |
VALIDATION_ERROR | Request validation failed | No |
MISSING_REQUIRED_FIELD | Required field is missing | No |
INVALID_FORMAT | Field format is invalid | No |
INVALID_VALUE | Invalid parameter value | No |
RATE_LIMIT_EXCEEDED | Too many requests in time window | Yes |
INSUFFICIENT_MARGIN | Not enough margin for trade | No |
ORDER_NOT_FOUND | Order does not exist | No |
OPERATION_TIMEOUT | Operation timed out | Yes |
Next Steps
- Get Subaccount - Get a single subaccount
- Get Delegated Signers - Get delegated signers for a single subaccount
- Get Positions - Detailed position information
- SubAccount Updates - Real-time account updates
- REST Alternative - REST API comparison
