> ## Documentation Index
> Fetch the complete documentation index at: https://docs.limitless.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner Accounts

> Create and manage sub-accounts with the TypeScript SDK

The `PartnerAccountService` creates sub-account profiles linked to the authenticated partner. Requires HMAC authentication with the `account_creation` scope.

## Access

```typescript theme={null}
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](/developers/programmatic-api#sub-account-modes) — the partner submits unsigned orders and the server signs them.

```typescript theme={null}
const account = await client.partnerAccounts.createAccount({
  displayName: 'user-alice',
  createServerWallet: true,
});

console.log(account.profileId); // numeric profile ID
console.log(account.account);   // wallet address
```

<Note>
  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.
</Note>

## 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.

```typescript theme={null}
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                                         |

<Warning>
  Do not send `x-on-behalf-of` to this endpoint. Results are always scoped to the authenticated partner behind the HMAC token.
</Warning>

## 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

```typescript theme={null}
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:

1. Poll `checkAllowances(profileId)`.
2. If `ready === true`, continue.
3. If targets are `missing` or `failed` with `retryable === true`, call `retryAllowances(profileId)`.
4. If retry returns `submitted` targets, poll `checkAllowances()` again after a short delay.
5. If retry returns `429`, wait `retryAfterSeconds`.
6. 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.

```typescript theme={null}
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);
```

<Note>
  `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.
</Note>

## 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:

```typescript theme={null}
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.
