Add Delegated Signer
Add a delegated signer to a subaccount, allowing another wallet address to perform authorized actions on behalf of the subaccount. This enables secure delegation of trading operations to automated systems, trading bots, or team members without sharing private keys. Multiple delegated signers can be added to a single subaccount.
Endpoint
POST https://papi.synthetix.io/v1/tradeRequest
Request Format
{
"params": {
"action": "addDelegatedSigner",
"subAccountId": "1867542890123456789",
"delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["trading"]
},
"nonce": 1735689600000,
"expiresAfter": 1735689900000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
params | object | Yes | Request parameters wrapper |
params.action | string | Yes | Must be "addDelegatedSigner" |
params.subAccountId | string | Yes | The subaccount ID to add the delegated signer to |
params.delegateAddress | string | Yes | Ethereum wallet address of the delegated signer (42-character hex format) |
params.permissions | string[] | Yes | Array of permission levels to grant. Currently supports: ["trading"] |
params.expiresAt | integer | No | Optional Unix timestamp (milliseconds) when the delegation expires |
expiresAfter | integer | No | Optional request expiration timestamp (milliseconds) |
| Parameter | Type | Required | Description |
|---|---|---|---|
nonce | uint64 | Yes* | Unix milliseconds timestamp (must be monotonically increasing) |
signature | object | Yes | EIP-712 signature object |
expiresAfter | uint64 | No | Unix seconds expiration timestamp (5x rate limit on stale cancels) |
:::info Common Parameters
These are top-level request parameters. The subAccountId parameter is specified within the params object (see each endpoint's parameter table).
:::
:::info SubAccountAction Endpoints
Endpoints using SubAccountAction signing (getPositions, getOpenOrders, getOrdersHistory, getTrades, getFundingPayments, getSubAccount, getDelegatedSigners, getBalanceUpdates) do not require the nonce parameter. Only signature and optional expiresAfter are needed.
:::
EIP-712 Signature Structure
The action object fields are signed using EIP-712. Note that when expiresAt is omitted in the request, it should be set to 0 (not null) in the EIP-712 message:
EIP-712 Type Definitions for Delegation
AddDelegatedSigner
const AddDelegatedSignerTypes = {
AddDelegatedSigner: [
{ name: "delegateAddress", type: "address" },
{ name: "subAccountId", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "expiresAfter", type: "uint256" },
{ name: "expiresAt", type: "uint256" },
{ name: "permissions", type: "string[]" }
]
}GetDelegatedSigners
Uses the standard SubAccountAction type (same as other read operations like getTrades, getPositions):
const SubAccountActionTypes = {
SubAccountAction: [
{ name: "subAccountId", type: "uint256" },
{ name: "action", type: "string" },
{ name: "expiresAfter", type: "uint256" }
]
}
// Message example:
const message = {
subAccountId: "1867542890123456789",
action: "getDelegatedSigners",
expiresAfter: 0 // Optional, use 0 if not expiring
}RemoveDelegatedSigner
const RemoveDelegatedSignerTypes = {
RemoveDelegatedSigner: [
{ name: "delegateAddress", type: "address" },
{ name: "subAccountId", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "expiresAfter", type: "uint256" }
]
}Example Typed Data for AddDelegatedSigner
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"AddDelegatedSigner": [
{ "name": "delegateAddress", "type": "address" },
{ "name": "subAccountId", "type": "uint256" },
{ "name": "nonce", "type": "uint256" },
{ "name": "expiresAfter", "type": "uint256" },
{ "name": "expiresAt", "type": "uint256" },
{ "name": "permissions", "type": "string[]" }
]
},
"primaryType": "AddDelegatedSigner",
"domain": {
"name": "Synthetix",
"version": "1",
"chainId": 1,
"verifyingContract": "0x0000000000000000000000000000000000000000"
},
"message": {
"delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"subAccountId": "1867542890123456789",
"nonce": 1735689600000,
"expiresAfter": 1735689900000,
"expiresAt": 0,
"permissions": ["trading"]
}
}Important Notes:
- Field order matters for EIP-712 - Fields must be in the exact order shown above for signature verification
delegateAddressis the wallet address being granted delegation permissionsexpiresAfteris the request expiration timestamp (when the request itself expires)expiresAtis when the delegation permission expires (use0for no expiration)- The
noncefield must be monotonically increasing per subaccount - All delegation operations must be signed by the master account owner
Response
Success Response
{
"status": "ok",
"response": {
"subAccountId": "1867542890123456789",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["trading"],
"expiresAt": null
},
"request_id": "5ccf215d37e3ae6d"
}Response Fields
| Field | Type | Description |
|---|---|---|
subAccountId | string | The subaccount ID the signer was added to |
walletAddress | string | The delegated signer's wallet address |
permissions | string[] | The permission levels granted |
expiresAt | integer | null | Expiration timestamp (null if no expiration) |
Error Response
{
"status": "error",
"error": {
"message": "Delegated signer already exists",
"code": "VALIDATION_ERROR"
},
"request_id": "5ccf215d37e3ae6d"
}Common Error Cases
{
"status": "error",
"error": {
"message": "Maximum delegated signers limit reached",
"code": "VALIDATION_ERROR"
},
"request_id": "5ccf215d37e3ae6d"
}{
"status": "error",
"error": {
"message": "Subaccount not found",
"code": "NOT_FOUND"
},
"request_id": "5ccf215d37e3ae6d"
}{
"status": "error",
"error": {
"message": "Cannot delegate to self",
"code": "VALIDATION_ERROR"
},
"request_id": "5ccf215d37e3ae6d"
}Code Examples
Add Trading Bot Signer
{
"params": {
"action": "addDelegatedSigner",
"subAccountId": "1867542890123456789",
"delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
"permissions": ["trading"]
},
"nonce": 1735689600000,
"expiresAfter": 1735689900000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}Add Team Member for Trading
{
"params": {
"action": "addDelegatedSigner",
"subAccountId": "1867542890123456789",
"delegateAddress": "0x8B3a9A6F8D1e2C4E5B7A9D0F1C3E5A7B9D1F3E5A",
"permissions": ["trading"]
},
"nonce": 1735689600001,
"expiresAfter": 1735689900000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}Add Temporary Delegated Signer with Expiration
{
"params": {
"action": "addDelegatedSigner",
"subAccountId": "1867542890123456789",
"delegateAddress": "0x9C4b8E7F0A2D3B6C5E8A1F3D5B7C9E1A3F5D7B9E",
"permissions": ["trading"],
"expiresAt": 1767225600000
},
"nonce": 1735689600002,
"expiresAfter": 1735689900000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}Implementation Notes
- Master Account Required: Only the master account owner can add delegated signers
- Multiple Signers: A subaccount can have multiple delegated signers simultaneously
- Unique Addresses: Each wallet address can only be delegated once per subaccount
- Trading Permission: The
["trading"]permission grants ability to place, modify, and cancel orders - Immediate Effect: Delegations take effect immediately upon successful creation
- Optional Expiration: Delegations can include an expiration timestamp after which they become invalid
- Revocable: Delegations can be revoked at any time using the
removeDelegatedSignerendpoint - Limits: Platform may enforce maximum number of delegated signers per subaccount
Security Considerations
- No Self-Delegation: Accounts cannot delegate to themselves
- Address Validation: Wallet addresses must be valid Ethereum addresses
- Trading Only: Delegated signers can only perform trading operations
- Signature Required: All delegation operations require valid EIP-712 signatures
- Master Control: Only the master account retains the ability to manage delegations
Delegate Object Structure
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
}Signing
All trading methods are signed using EIP-712. Each successful trading request will contain:
- A piece of structured data that includes the sender address
- A signature of the hash of that structured data, signed by the sender
For detailed information on EIP-712 signing, see EIP-712 Signing.
Nonce Management
The nonce system prevents replay attacks and ensures order uniqueness:
- Use current timestamp in milliseconds as nonce
- Each nonce must be greater than the previous one
- Recommended: Use
Date.now()or equivalent - If nonce conflicts occur, increment by 1 and retry
:::note SubAccountAction Exception
SubAccountAction endpoints (getPositions, getOpenOrders, getOrdersHistory, getTrades, getFundingPayments, getSubAccount, getDelegatedSigners, getBalanceUpdates) do not require a nonce. Only the signature and optional expiresAfter parameters are needed.
:::
Error Handling
| 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 |