Skip to content

Create Subaccount (WebSocket)

Create a new trading subaccount under the authenticated wallet address through the WebSocket connection. Subaccounts allow for isolated trading strategies, separate margin management, and organized position tracking within a single master account.

Endpoint

ws.send() wss://api.synthetix.io/v1/ws/trade

Request

Request Format

{
  "id": "create-subaccount-1",
  "method": "post",
  "params": {
    "action": "createSubaccount",
    "name": "Trading Bot Strategy",
    "nonce": 1735689600000,
    "expiresAfter": 1735689900,
    "signature": {
      "v": 28,
      "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
    }
  }
}

Parameters

Request Parameters

ParameterTypeRequiredDescription
idstringYesClient-generated unique request identifier
methodstringYesMust be "post"
paramsobjectYesContains all parameters for the request

Params Object

ParameterTypeRequiredDescription
actionstringYesMust be "createSubaccount"
namestringNoOptional display name for the new subaccount (max 50 chars)
nonceintegerYesPositive integer, incrementing nonce
expiresAfterintegerNoOptional expiration timestamp in seconds (0 = no expiration)
signatureobjectYesEIP-712 signature for wallet authentication

Important: This endpoint must be sent over a WebSocket connection already authenticated to an owned subaccount. The EIP-712 payload signs that authenticated subaccount as masterSubAccountId to prove ownership before creating the new subaccount.

Naming Guidelines

  • Optional Parameter: Name can be omitted for system-generated naming
  • Character Limit: Maximum 50 characters when specified
  • Default Behavior: If no name provided, system may generate a default identifier

Examples: "Main Trading", "DCA Strategy", "Arbitrage Bot", "Risk Management"

Response Format

Success Response (New Account)

When a subaccount is first created, subAccountId, subAccountName, and accountLimits are populated. All other fields return zero/null values:

{
  "id": "create-subaccount-1",
  "status": 200,
  "result": {
    "subAccountId": "1867542890123456790",
    "masterAccountId": null,
    "subAccountName": "Trading Bot Strategy",
    "collaterals": null,
    "crossMarginSummary": {
      "accountValue": "",
      "availableMargin": "",
      "totalUnrealizedPnl": "",
      "maintenanceMargin": "",
      "initialMargin": "",
      "withdrawable": "",
      "adjustedAccountValue": "",
      "debt": ""
    },
    "positions": null,
    "marketPreferences": {
      "leverages": null
    },
    "feeRates": {
      "makerFeeRate": "",
      "takerFeeRate": "",
      "tierName": ""
    },
    "accountLimits": {
      "maxBorrowCapacity": "100000",
      "maxOrdersPerMarket": 50,
      "maxSubAccounts": 10,
      "maxTotalOrders": 200
    }
  }
}

Response Fields

FieldTypeDescription
subAccountIdstringSystem-generated unique identifier for the new subaccount
masterAccountIdstring | nullMaster account ID (returns null)
subAccountNamestringDisplay name provided in request (or empty if not specified)
collateralsarray | nullCollateral balances (returns null for new accounts)
collaterals[].adjustedCollateralValuestringAdjusted collateral value after applying haircuts
collaterals[].calculatedAtintegerUnix millisecond timestamp of the collateral price calculation (0 for USDT)
collaterals[].collateralValuestringRaw collateral value
collaterals[].pendingWithdrawstringAmount pending withdrawal
collaterals[].pricestringCollateral price used for valuation
collaterals[].quantitystringTotal collateral quantity held
collaterals[].symbolstringCollateral asset symbol
collaterals[].withdrawablestringAmount available to withdraw
crossMarginSummaryobjectCross-margin account summary (returns empty strings for new accounts)
crossMarginSummary.debtstringUSDT debt after positive unrealized PnL offset
positionsarray | nullOpen positions (returns null for new accounts)
marketPreferencesobjectMarket-specific preferences like leverage
feeRatesobjectFee rate information
accountLimitsobjectAccount limit information
accountLimits.maxBorrowCapacitystringMaximum borrow capacity in USDT
accountLimits.maxOrdersPerMarketintegerMaximum open orders allowed per market
accountLimits.maxSubAccountsintegerMaximum number of subaccounts allowed for the wallet
accountLimits.maxTotalOrdersintegerMaximum total open orders across all markets

Error Response

{
  "id": "create-subaccount-1",
  "status": 400,
  "result": null,
  "error": {
    "code": 400,
    "message": "Subaccount limit reached"
  }
}

Implementation Example

import { ethers } from 'ethers';
 
async function createSubaccount(ws, signer, masterSubAccountId, name = "") {
  const nonce = Date.now();
 
  const domain = {
    name: "Synthetix",
    version: "1",
    chainId: 1,
    verifyingContract: "0x0000000000000000000000000000000000000000"
  };
 
  const types = {
    CreateSubaccount: [
      { name: "masterSubAccountId", type: "uint256" },
      { name: "name", type: "string" },
      { name: "nonce", type: "uint256" },
      { name: "expiresAfter", type: "uint256" }
    ]
  };
 
  const expiresAfter = 0; // Set to a future timestamp to enforce expiry, or 0 for no expiry
 
  const message = {
    masterSubAccountId: BigInt(masterSubAccountId),
    name,
    nonce: BigInt(nonce),
    expiresAfter: BigInt(expiresAfter)
  };
 
  const sig = await signer.signTypedData(domain, types, message);
  const signature = ethers.Signature.from(sig);
 
  ws.send(JSON.stringify({
    id: `create-subaccount-${Date.now()}`,
    method: "post",
    params: {
      action: "createSubaccount",
      name,
      nonce,
      expiresAfter,
      signature: { v: signature.v, r: signature.r, s: signature.s }
    }
  }));
}
 
// Usage: Create a new trading subaccount after authenticating the WebSocket as the owner
await createSubaccount(ws, signer, "1867542890123456789", "Grid Trading Bot");

Code Examples

Create Basic Subaccount

{
  "id": "create-basic",
  "method": "post",
  "params": {
    "action": "createSubaccount",
    "name": "Main Trading",
    "nonce": 1735689600000,
    "expiresAfter": 1735689900,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Create Strategy-Specific Subaccount

{
  "id": "create-strategy",
  "method": "post",
  "params": {
    "action": "createSubaccount",
    "name": "Grid Trading Bot",
    "nonce": 1735689600001,
    "expiresAfter": 1735689901,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Create Subaccount Without Name

{
  "id": "create-unnamed",
  "method": "post",
  "params": {
    "action": "createSubaccount",
    "nonce": 1735689600002,
    "expiresAfter": 1735689902,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Implementation Notes

  • Ownership Proof: Authenticate the WebSocket as an owned subaccount, then sign that authenticated subaccount as masterSubAccountId in the EIP-712 payload
  • Immediate Availability: Subaccount is immediately available for trading after creation
  • Account Limits: Platform may enforce maximum number of subaccounts per wallet
  • Initial State: New subaccounts have zero balances and no positions
  • Security Inheritance: Each subaccount inherits master wallet security settings

Common Errors

Error CodeMessageDescription
400Subaccount limit reachedMaximum subaccounts for this wallet exceeded
400Invalid subaccount nameName exceeds 50 characters or contains invalid characters
401Authentication failedInvalid signature
Error CodeDescriptionRetryable
UNAUTHORIZEDEIP-712 signature validation failedNo
VALIDATION_ERRORRequest validation failedNo
MISSING_REQUIRED_FIELDRequired field is missingNo
INVALID_FORMATField format is invalidNo
INVALID_VALUEInvalid parameter valueNo
RATE_LIMIT_EXCEEDEDToo many requests in time windowYes
INSUFFICIENT_MARGINNot enough margin for tradeNo
ORDER_NOT_FOUNDOrder does not existNo
OPERATION_TIMEOUTOperation timed outYes

Next Steps