Skip to content

Get Delegated Signers (WebSocket)

Retrieve a list of all delegated signers for a specific subaccount through the WebSocket connection. This endpoint provides visibility into which wallet addresses have been granted permissions to act on behalf of the subaccount, along with their permission levels and delegation details.

Endpoint

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

Request

Request Format

{
  "id": "delegated-signers-1",
  "method": "post",
  "params": {
    "action": "getDelegatedSigners",
    "subAccountId": "1867542890123456789",
    "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 "getDelegatedSigners"
subAccountIdstringYesSubaccount identifier
expiresAfterintegerNoOptional expiration timestamp in seconds
signatureobjectYesEIP-712 signature using SubAccountAction type
Important Notes:
  • This endpoint uses SubAccountAction EIP-712 type (no nonce required)
  • Both master account owners and delegated signers can view the delegation list

EIP-712 Signature

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: "1867542890123456789",
  action: "getDelegatedSigners",
  expiresAfter: 0
};
 
const signature = await signer._signTypedData(domain, types, message);

Response Format

Success Response

{
  "id": "delegated-signers-1",
  "status": 200,
  "result": {
    "delegatedSigners": [
      {
        "subAccountId": "1867542890123456789",
        "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
        "permissions": ["trading"],
        "expiresAt": null
      },
      {
        "subAccountId": "1867542890123456789",
        "walletAddress": "0x8B3a9A6F8D1e2C4E5B7A9D0F1C3E5A7B9D1F3E5A",
        "permissions": ["trading"],
        "expiresAt": 1767225600000
      }
    ]
  }
}

Empty Response

{
  "id": "delegated-signers-1",
  "status": 200,
  "result": {
    "delegatedSigners": []
  }
}

Error Response

{
  "id": "delegated-signers-1",
  "status": 404,
  "result": null,
  "error": {
    "code": 404,
    "message": "Subaccount not found"
  }
}

Response Fields

FieldTypeDescription
delegatedSignersarrayArray of delegated signer objects

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.

FieldTypeDescription
subAccountIdstringSubaccount ID this delegation applies to
walletAddressstringEthereum wallet address of the delegated signer (42-character hex format)
permissionsstring[]Array of permission levels granted. Currently supports: ["trading"]
expiresAtinteger | nullUnix timestamp (milliseconds) when the delegation expires. null indicates no expiration
addedBystring | omittedWallet address that created this delegation. Omitted for pre-migration records where the creator was not recorded

Example Delegate Object

{
  "subAccountId": "1867542890123456789",
  "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
  "permissions": ["trading"],
  "expiresAt": null,
  "addedBy": "0x1111111111111111111111111111111111111111"
}

Example with Expiration

{
  "subAccountId": "1867542890123456789",
  "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
  "permissions": ["trading"],
  "expiresAt": 1767225600000
}

Code Examples

Get All Delegated Signers

{
  "id": "get-delegates",
  "method": "post",
  "params": {
    "action": "getDelegatedSigners",
    "subAccountId": "1867542890123456789",
    "expiresAfter": 0,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Implementation Example

class DelegationQuery {
  constructor(ws, signer) {
    this.ws = ws;
    this.signer = signer;
    this.pendingRequests = new Map();
  }
 
  async getDelegatedSigners(subAccountId, expiresAfter = 0) {
    const signature = await this.signSubAccountAction(subAccountId, "getDelegatedSigners", expiresAfter);
 
    const request = {
      id: `delegated-signers-${Date.now()}`,
      method: "post",
      params: {
        action: "getDelegatedSigners",
        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);
    });
  }
}
 
// Usage
const query = new DelegationQuery(ws, signer);
const result = await query.getDelegatedSigners("1867542890123456789");
 
console.log(`Found ${result.delegatedSigners.length} delegated signers`);
result.delegatedSigners.forEach(delegate => {
  console.log(`Address: ${delegate.walletAddress}`);
  console.log(`Permissions: ${delegate.permissions.join(', ')}`);
  console.log(`Expires: ${delegate.expiresAt || 'Never'}`);
});

Use Cases

  • Team Management: View all team members with access to a trading account
  • Security Audit: Regular review of who has access to perform operations
  • Access Control UI: Build interfaces showing current permissions
  • Bot Management: Track which automated systems have trading access
  • Compliance: Maintain records of account access for regulatory purposes

Implementation Notes

  • Access Control: Both master account owners and delegated signers can view the delegation list
  • Real-time Data: Returns current active delegations only
  • Expiration Handling: Expired delegations are automatically excluded from results
  • No Pagination: All delegated signers are returned in a single response

Common Errors

Error CodeMessageDescription
404Subaccount not foundInvalid subaccount ID
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