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/tradeRequest
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
| 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 "getDelegatedSigners" |
subAccountId | string | Yes | Subaccount identifier |
expiresAfter | integer | No | Optional expiration timestamp in seconds |
signature | object | Yes | EIP-712 signature using SubAccountAction type |
- This endpoint uses
SubAccountActionEIP-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
| Field | Type | Description |
|---|---|---|
delegatedSigners | array | Array 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.
| 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. Currently supports: ["trading"] |
expiresAt | integer | null | Unix timestamp (milliseconds) when the delegation expires. null indicates no expiration |
Example Delegate Object
{
"subAccountId": "1867542890123456789",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["trading"],
"expiresAt": null
}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 Code | Message | Description |
|---|---|---|
| 404 | Subaccount not found | Invalid subaccount ID |
| 401 | Authentication failed | Invalid signature |
| Error | Description |
|---|---|
| Invalid signature | EIP-712 signature validation failed |
| Invalid market symbol | Market symbol not recognized |
| Nonce already used | Nonce must be greater than previous value |
| Rate limit exceeded | Too many requests in time window |
| Request expired | expiresAfter timestamp has passed |
Next Steps
- Add Delegated Signer - Add new delegation
- Remove Delegated Signer - Revoke delegation
- Get Subaccount - Account details
- REST Alternative - REST API comparison