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

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

## Access

```python theme={null}
from limitless_sdk import Client, HMACCredentials

client = Client(
    base_url="https://api.limitless.exchange",
    hmac_credentials=HMACCredentials(token_id=token_id, secret=secret),
)

# Use client.partner_accounts.*
```

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

```python theme={null}
from limitless_sdk import CreatePartnerAccountInput

account = await client.partner_accounts.create_account(
    CreatePartnerAccountInput(
        display_name="user-alice",
        create_server_wallet=True,
    )
)

print(account.profile_id)  # numeric profile ID
print(account.account)     # wallet address
```

<Note>
  New server wallets should be checked with `check_allowances()` before the first delegated trade. If retryable targets are missing or failed, call `retry_allowances()` and poll again.
</Note>

## List and recover sub-accounts

Use `list_accounts()` 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.

```python theme={null}
from limitless_sdk import ListPartnerAccountsParams

accounts = await client.partner_accounts.list_accounts(
    ListPartnerAccountsParams(limit=25, page=1)
)

for account in accounts.data:
    print(account.profile_id, account.account, account.display_name)

recovered = await client.partner_accounts.list_accounts(
    ListPartnerAccountsParams(
        account="0x1676716Ef7F19B5C5d690631CB57cf0bFD900A3d",
        limit=25,
        page=1,
    )
)

profile_id = recovered.data[0].profile_id if recovered.data else None
```

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

* `check_allowances(profile_id)` calls `GET /profiles/partner-accounts/:profileId/allowances`
* `retry_allowances(profile_id)` calls `POST /profiles/partner-accounts/:profileId/allowances/retry`
* both methods require HMAC credentials with `account_creation` and `delegated_signing`
* `profile_id` is the child/server-wallet profile id

```python theme={null}
from limitless_sdk import ConflictError, RateLimitError

allowances = await client.partner_accounts.check_allowances(account.profile_id)

if not allowances.ready:
    retryable_targets = [
        target
        for target in allowances.targets
        if target.retryable and target.status in {"missing", "failed"}
    ]

    if retryable_targets:
        try:
            allowances = await client.partner_accounts.retry_allowances(account.profile_id)
        except RateLimitError as error:
            print(error.response_data.get("retryAfterSeconds"))
        except ConflictError:
            print("Retry already running; poll check_allowances again shortly.")

if any(target.status == "submitted" for target in allowances.targets):
    # A sponsored tx/user operation was submitted by this retry request.
    # Poll check_allowances() again after a short delay to observe confirmed chain state.
    pass
```

Recommended partner flow:

1. Poll `check_allowances(profile_id)`.
2. If `ready is True`, continue.
3. If targets are `missing` or `failed` with `retryable=True`, call `retry_allowances(profile_id)`.
4. If retry returns `submitted` targets, poll `check_allowances()` again after a short delay.
5. If retry returns `429`, wait `retryAfterSeconds`.
6. If retry returns `409`, wait briefly and call `check_allowances()` 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.

```python theme={null}
import os
from limitless_sdk import PartnerWithdrawalAddressInput

identity_token = os.environ["PRIVY_IDENTITY_TOKEN"]
treasury_address = "0x0F3262730c909408042F9Da345a916dc0e1F9787"

entry = await client.partner_accounts.add_withdrawal_address(
    identity_token,
    PartnerWithdrawalAddressInput(address=treasury_address, label="treasury"),
)

print(entry.destination_address)

await client.partner_accounts.delete_withdrawal_address(identity_token, treasury_address)
```

<Note>
  `add_withdrawal_address()` and `delete_withdrawal_address()` 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:

```python theme={null}
from limitless_sdk import CreatePartnerAccountInput, CreatePartnerAccountEOAHeaders

account = await client.partner_accounts.create_account(
    CreatePartnerAccountInput(display_name="user-bob"),
    eoa_headers=CreatePartnerAccountEOAHeaders(
        account="0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
        signing_message="0x...",
        signature="0x...",
    ),
)
```

## Validation

* `display_name` 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 `display_name` length locally before sending the request.
