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

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

## Access

```rust theme={null}
use limitless_exchange_rust_sdk::{Client, HmacCredentials};

let http = Client::builder()
    .hmac_credentials(HmacCredentials {
        token_id: token_id.to_string(),
        secret: secret.to_string(),
    })
    .build()?;

let client = Client::from_http_client(http)?;

// Use client.partner_accounts.*
```

## Server wallet mode

Set `create_server_wallet` to create a managed wallet for the sub-account. This enables delegated signing:

```rust theme={null}
use limitless_exchange_rust_sdk::CreatePartnerAccountInput;

let account = client
    .partner_accounts
    .create_account(
        &CreatePartnerAccountInput {
            display_name: Some("user-alice".to_string()),
            create_server_wallet: Some(true),
        },
        None,
    )
    .await?;

println!("{}", account.profile_id);
println!("{}", account.account);
```

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

```rust theme={null}
use limitless_exchange_rust_sdk::ListPartnerAccountsParams;

let accounts = client
    .partner_accounts
    .list_accounts(&ListPartnerAccountsParams {
        limit: Some(25),
        page: Some(1),
        ..Default::default()
    })
    .await?;

for account in accounts.data {
    println!("{} {} {:?}", account.profile_id, account.account, account.display_name);
}

let recovered = client
    .partner_accounts
    .list_accounts(&ListPartnerAccountsParams {
        account: Some("0x1676716Ef7F19B5C5d690631CB57cf0bFD900A3d".to_string()),
        limit: Some(25),
        page: Some(1),
    })
    .await?;

let profile_id = recovered.data.first().map(|account| account.profile_id);
```

| 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

```rust theme={null}
use limitless_exchange_rust_sdk::{
    LimitlessError, PARTNER_ACCOUNT_ALLOWANCE_STATUS_FAILED,
    PARTNER_ACCOUNT_ALLOWANCE_STATUS_MISSING, PARTNER_ACCOUNT_ALLOWANCE_STATUS_SUBMITTED,
};

let mut allowances = client
    .partner_accounts
    .check_allowances(account.profile_id)
    .await?;

if !allowances.ready {
    let retryable = allowances.targets.iter().any(|target| {
        target.retryable
            && matches!(
                target.status.as_str(),
                PARTNER_ACCOUNT_ALLOWANCE_STATUS_MISSING
                    | PARTNER_ACCOUNT_ALLOWANCE_STATUS_FAILED
            )
    });

    if retryable {
        match client
            .partner_accounts
            .retry_allowances(account.profile_id)
            .await
        {
            Ok(retried) => allowances = retried,
            Err(LimitlessError::Api(err)) if err.status == 429 => {
                println!("retryAfterSeconds is in err.data");
            }
            Err(LimitlessError::Api(err)) if err.status == 409 => {
                println!("Retry already running; poll check_allowances again shortly.");
            }
            Err(err) => return Err(err.into()),
        }
    }
}

if allowances
    .targets
    .iter()
    .any(|target| target.status == PARTNER_ACCOUNT_ALLOWANCE_STATUS_SUBMITTED)
{
    // A sponsored tx/user operation was submitted by this retry request.
    // Poll check_allowances again after a short delay to observe confirmed chain state.
}
```

Recommended partner flow:

1. Poll `check_allowances(profile_id)`.
2. If `ready == 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.

```rust theme={null}
use limitless_exchange_rust_sdk::PartnerWithdrawalAddressInput;

let identity_token = std::env::var("PRIVY_IDENTITY_TOKEN")?;
let treasury_address = "0x0F3262730c909408042F9Da345a916dc0e1F9787";

let entry = client
    .partner_accounts
    .add_withdrawal_address(
        &identity_token,
        &PartnerWithdrawalAddressInput {
            address: treasury_address.to_string(),
            label: Some("treasury".to_string()),
        },
    )
    .await?;

println!("{}", entry.destination_address);

client
    .partner_accounts
    .delete_withdrawal_address(&identity_token, treasury_address)
    .await?;
```

<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

For externally-owned accounts, pass ownership-verification headers:

```rust theme={null}
use limitless_exchange_rust_sdk::{
    CreatePartnerAccountEoaHeaders, CreatePartnerAccountInput,
};

let account = client
    .partner_accounts
    .create_account(
        &CreatePartnerAccountInput {
            display_name: Some("user-bob".to_string()),
            create_server_wallet: Some(false),
        },
        Some(&CreatePartnerAccountEoaHeaders {
            account: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed".to_string(),
            signing_message: "0x...".to_string(),
            signature: "0x...".to_string(),
        }),
    )
    .await?;
```

## Validation

* `display_name` is optional and capped at 44 characters
* when `create_server_wallet` is not `Some(true)`, EOA headers are required
* the SDK validates `display_name` length locally before sending the request
* `409 Conflict` is returned if a profile already exists for the target address

<Warning>
  Server-wallet sub-accounts and EOA sub-accounts use different signing models. Use server-wallet mode for delegated signing, and EOA mode when the end user will sign orders themselves.
</Warning>
