The PartnerAccountService creates sub-account profiles linked to the authenticated partner. Requires HMAC authentication with the account_creation scope.
Access
import { Client } from '@limitless-exchange/sdk';
const client = new Client({
baseURL: 'https://api.limitless.exchange',
hmacCredentials: { tokenId, secret },
});
// Use client.partnerAccounts.*
Server wallet mode
Creates a managed Privy wallet for the sub-account. Enables delegated signing — the partner submits unsigned orders and the server signs them.
const account = await client.partnerAccounts.createAccount({
displayName: 'user-alice',
createServerWallet: true,
});
console.log(account.profileId); // numeric profile ID
console.log(account.account); // wallet address
New server wallets should be checked with checkAllowances() before the first delegated trade. If retryable targets are missing or failed, call retryAllowances() and poll again.
List and recover sub-accounts
Use listAccounts() to list partner-owned sub-accounts or recover a specific child profile by wallet address. This calls GET /profiles/partner-accounts and requires HMAC credentials with the account_creation scope.
const accounts = await client.partnerAccounts.listAccounts({
limit: 25,
page: 1,
});
for (const account of accounts.data) {
console.log(account.profileId, account.account, account.displayName);
}
const recovered = await client.partnerAccounts.listAccounts({
account: '0x1676716Ef7F19B5C5d690631CB57cf0bFD900A3d',
limit: 25,
page: 1,
});
const profileId = recovered.data[0]?.profileId;
| Parameter | Description |
|---|
account | Optional exact EVM address filter for recovering a known child account |
limit | Optional page size. The API caps values above 25 to 25 |
page | Optional 1-indexed page number |
Do not send x-on-behalf-of to this endpoint. Results are always scoped to the authenticated partner behind the HMAC token.
Allowance recovery
Server-wallet sub-accounts need delegated-trading approvals before they can trade. The partner allowance helpers use the Partner API only:
checkAllowances(profileId) calls GET /profiles/partner-accounts/:profileId/allowances
retryAllowances(profileId) calls POST /profiles/partner-accounts/:profileId/allowances/retry
- both methods require HMAC credentials with
account_creation and delegated_signing
profileId is the child/server-wallet profile id
import { APIError, RateLimitError } from '@limitless-exchange/sdk';
let allowances = await client.partnerAccounts.checkAllowances(account.profileId);
if (!allowances.ready) {
const retryableTargets = allowances.targets.filter(
(target) =>
target.retryable &&
(target.status === 'missing' || target.status === 'failed'),
);
if (retryableTargets.length > 0) {
try {
allowances = await client.partnerAccounts.retryAllowances(account.profileId);
} catch (error) {
if (error instanceof RateLimitError) {
console.log(error.data?.retryAfterSeconds);
} else if (error instanceof APIError && error.status === 409) {
console.log('Retry already running; poll checkAllowances again shortly.');
} else {
throw error;
}
}
}
}
if (allowances.targets.some((target) => target.status === 'submitted')) {
// A sponsored tx/user operation was submitted by this retry request.
// Poll checkAllowances() again after a short delay to observe confirmed chain state.
}
Recommended partner flow:
- Poll
checkAllowances(profileId).
- If
ready === true, continue.
- If targets are
missing or failed with retryable === true, call retryAllowances(profileId).
- If retry returns
submitted targets, poll checkAllowances() again after a short delay.
- If retry returns
429, wait retryAfterSeconds.
- If retry returns
409, wait briefly and call checkAllowances() again.
Withdrawal address allowlist
Explicit treasury destinations for server-wallet withdrawals must be allowlisted on the authenticated partner profile unless the destination is already the partner account or partner smart wallet. Allowlist management uses a Privy identity token, not API-token/HMAC auth.
const identityToken = process.env.PRIVY_IDENTITY_TOKEN!;
const treasuryAddress = '0x0F3262730c909408042F9Da345a916dc0e1F9787';
const entry = await client.partnerAccounts.addWithdrawalAddress(identityToken, {
address: treasuryAddress,
label: 'treasury',
});
console.log(entry.destinationAddress);
await client.partnerAccounts.deleteWithdrawalAddress(identityToken, treasuryAddress);
addWithdrawalAddress() and deleteWithdrawalAddress() call POST /portfolio/withdrawal-addresses and DELETE /portfolio/withdrawal-addresses/:address with the identity: Bearer <token> header. POST /portfolio/withdraw still uses HMAC auth with the withdrawal scope.
EOA mode
Creates a profile for an externally-owned address. The end user manages their own keys and signs orders themselves.
EOA mode requires wallet ownership verification headers:
const account = await client.partnerAccounts.createAccount(
{
displayName: 'user-bob',
},
{
account: '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed',
signingMessage: '0x...',
signature: '0x...',
},
);
| Header | Description |
|---|
account | Checksummed Ethereum address (EIP-55) |
signingMessage | Hex-encoded signing message |
signature | Hex-encoded signature from the wallet |
Validation
displayName is optional, max 44 characters. Defaults to the wallet address if omitted.
- Returns
409 Conflict if a profile already exists for the target address.
- Cannot create a sub-account for the partner’s own address.
- The SDK validates
displayName length locally before sending the request.