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

> Buy curated Packs and claim winnings for managed server-wallet accounts, with durable retries and allowance checks.

Partners can buy a Pack from the public catalogue for an owned managed server-wallet sub-account, then claim its winnings after settlement. The backend handles signing and sponsored on-chain submission. Custom baskets and RFQ purchases are outside this API, and partner revenue share is not included.

Use SDK **1.3.0 or later**: [TypeScript](/developers/sdk/typescript/packs.md), [Python](/developers/sdk/python/packs.md), [Go](/developers/sdk/go/packs.md), or [Rust](/developers/sdk/rust/packs.md).

## Authentication and account selection

Keep partner credentials on your server. Every `/parlay/partner/*` route requires [HMAC authentication](/developers/authentication.md) with `trading` and `delegated_signing`. Allowance checks and repair additionally require `account_creation`.

| Request | Select the managed account |
| - | - |
| Public catalogue list, search, detail | No authentication required |
| Partner quote | No account selector |
| Partner buy, claim, claim confirmation | `onBehalfOf` in the JSON body |
| Partner position list and detail | `x-on-behalf-of` request header |

Do not set `x-on-behalf-of` as a global client header. The SDKs apply delegation to position reads and put the account ID in the body for writes. A pack ID identifies the catalogue product; a position ID identifies one purchase.

## Buy workflow

### 1. Choose a Pack

Use [List Packs](/api-reference/packs/list-packs.md), [Search Packs](/api-reference/packs/search-packs.md), or [Get Pack](/api-reference/packs/get-pack.md). Listing and search return active, eligible Packs within their trade window. Detail can return a closed Pack, so fetching a Pack by ID does not guarantee it can be bought.

The catalogue's `payout` is a display estimate for a 10 USDC stake, expressed in human-readable USDC. Request a partner quote for the intended stake instead of copying that estimate into a buy request.

### 2. Confirm the Pack vault allowance

Before preparing a short-lived quote and buy intent:

1. Call [Check Partner Account Allowances](/api-reference/partner-accounts/check-allowances.md).
2. Find the `USDC_ALLOWANCE` target labelled `parlay-treasury-vault`.
3. Require `confirmed: true` and `status: "confirmed"` for that target.
4. If approval is missing, call [Retry Partner Account Allowances](/api-reference/partner-accounts/retry-allowances.md), then poll GET with a bounded wait until confirmed. A `submitted` target is still pending.

Repair follows the existing unlimited-approval policy and may repair other missing trading approvals too. If the vault target is absent, stop and check the environment's Pack vault configuration. Do not treat an empty target list or a generic readiness flag as proof of Pack approval.

When the wallet already has sufficient USDC allowance, buying skips permit calls. Code-bearing managed wallets, including delegated EIP-7702 wallets, require a standing vault allowance; they cannot use the EOA permit fallback. An insufficient allowance can return `409 PACK_ALLOWANCE_REQUIRED` with `tokenAddress`, `spender`, and `requiredAllowance`. Compare these with the allowance target when diagnosing a failure.

### 3. Quote and save a purchase request

Call [Quote Pack](/api-reference/packs/quote-pack.md):

```json theme={null}
{
  "packId": "22222222-2222-4222-8222-222222222222",
  "stakeAmount": "10000000"
}
```

Amounts in quote and buy requests are **integer micro-USDC strings**: `"1000000"` is 1 USDC. Avoid floating-point conversions. An ineligible quote can contain only:

```json theme={null}
{
  "eligible": false,
  "ineligibleReasons": ["The requested stake cannot be filled"]
}
```

Always check `eligible` before using optional pricing fields. An eligible quote is advisory; it does not reserve a price or liquidity.

Build the buy request from the quote:

| Field | Meaning |
| - | - |
| `onBehalfOf` | Owned managed profile ID, positive integer |
| `packId` | Catalogue Pack UUID |
| `stakeAmount` | Positive integer micro-USDC string |
| `minPayoutAmount` | Minimum acceptable total payout, in integer micro-USDC |
| `deadline` | Unix seconds as a string; future and at most 480 seconds away on first submission |
| `idempotencyKey` | Stable, unique purchase identifier; 1–128 printable ASCII characters without spaces |

The SDK payout helper floors `potentialPayout × (10000 − slippageBps) / 10000` using integer arithmetic. For example, 100 basis points allows a 1% reduction in total payout. This is a payout floor, not an extra fee.

**Persist the entire request before submitting it.** Associate it with your environment, partner identity, managed profile, and application purchase ID. Enforce one saved request per intended purchase so concurrent workers cannot create different keys. Keep credentials out of the journal.

### 4. Submit and wait for minting

Call [Buy Pack](/api-reference/packs/buy-pack.md) with the saved request. Store the returned position ID and transaction hash when available.

| Buy operation status | Position status | Action |
| - | - | - |
| `COMPLETED` | `ACTIVE`, `WON`, or `LOST` | Purchase has completed; retain the position ID |
| `PENDING` | `PENDING` | Poll the partner position endpoint |
| `RECOVERING` | `FAILED` | Continue bounded polling; reconciliation can reactivate the position |
| `FAILED` | `VAULT_REVERTED` | Terminal mint failure: vault transaction reverted |

> [!WARNING]
>
> Position `FAILED` is recoverable. It does not prove the purchase was never submitted. Starting a second purchase with a new key while the first is recovering can buy the Pack twice.

Use partner position reads to track in-flight purchases; the public portfolio excludes pending and failed positions. All four SDKs provide bounded mint polling. If polling expires, replay the **identical saved buy request**, including its original deadline, to obtain the backend's recovery state.

A timeout, disconnect, 5xx response, or locally expired deadline is not permission to change the request. Replaying the same key and body retrieves or resumes the original operation. Changing a field while keeping the key is invalid; changing the key creates a separate purchase.

## Recover buy errors

Use each SDK's typed Pack error classifier to read the API's recovery metadata. Classification does not retry, repair an allowance, or change an idempotency key.

| Signal | Recovery |
| - | - |
| `409 PRICE_DRIFTED` | Request a fresh quote, save a new request and key, then submit |
| Explicit 4xx `submissionState: "NOT_SUBMITTED"` | No broadcast occurred. Fix the reported cause, then save a fresh quote, deadline, and key. The old key remains bound to the failed position |
| `409 PACK_ALLOWANCE_REQUIRED` | Repair and confirm the reported vault allowance, then replay the saved buy. If replay explicitly rejects the intent as expired, renew as below |
| `401` with the exact message `Intent expired — please retry` | The server explicitly rejected the expired intent. Archive the old request and save a fresh quote, deadline, and key |
| `409` with `Another Pack operation is in progress for this wallet; retry the same request` | Wait, then retry the identical request |
| `429` | Wait at least `retryAfterSeconds`, then retry the identical request |
| `422` `Insufficient USDC balance` | The managed wallet holds less USDC than the stake. Fund it, then replay the saved request while its deadline is valid, or save a fresh quote, deadline, and key |
| Network error, timeout, 5xx, or any unclassified result | Retain the request and reconcile/replay it unchanged |

Do not interpret a generic `401` as an expired-intent rejection. If an error includes `NOT_SUBMITTED` as well as a cause code, use the explicit submission state to choose the recovery path.

## Claim a winning position

After the markets settle, read the position and require `status: "WON"`. Claim using its **position ID**, not its catalogue Pack ID:

```http theme={null}
POST /parlay/partner/positions/{positionId}/claim
Content-Type: application/json
```

```json theme={null}
{
  "onBehalfOf": 12345
}
```

Include freshly signed HMAC headers on each call. Repeat the same claim request to advance the durable operation:

| Claim status | Meaning |
| - | - |
| `APPROVAL_PENDING` | Sponsored conditional-token approval is pending |
| `CLAIM_PENDING` | Redemption is pending |
| `CLAIMED` | Redemption is confirmed; `txHash` identifies the claim transaction |

The SDK `claim` helper repeats this workflow with a bounded wait. `claimOnce` / `claim_once` / `ClaimOnce` performs a single step when you manage scheduling yourself. All claim helpers surface API errors; they do not automatically retry every error.

* On `429` or a wallet-busy conflict, wait and resume the same position's claim.
* On `PACK_TRANSACTION_REVERTED`, inspect the failed transaction, address the cause, and resume the same claim.
* On `PACK_SUBMISSION_UNKNOWN`, stop blind retries and reconcile the on-chain submission.

For a lost submission response, call [Confirm Claim](/api-reference/packs/confirm-claim.md) with the **mined factory redemption transaction hash** and the same `onBehalfOf`. The backend verifies the successful receipt and redemption event against the expected token and recipient. Do not pass the buy hash, approval hash, or `userOperationHash`.

The payout goes to the managed wallet. A sponsored transaction's sender may be a bundler; inspect receipt events and transfers for the recipient. The position's settlement status remains `WON` after claiming: use the claim response's `CLAIMED` status and the position's `claimTxHash` to verify completion.

## Pacing and polling

The partner routes share a limit of **10 requests per 10 seconds per partner owner**, across sub-accounts and workers. On `429`, the response includes `retryAfterSeconds: 10`.

Reuse one SDK client per partner. Its Packs service serializes calls with at least 1.1 seconds between starts and defers subsequent calls after a `429`. Coordinate separate clients and processes yourself. This pacing does not automatically replay a failed write.

Mint and claim polling default to 60 attempts with a 5-second interval. TypeScript, Python, and Rust expose polling options; Go supports cancellation through the request context. When a bounded wait ends, retain the purchase or claim identity and resume it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.