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

# Quick Buy (Self-Trade)

> Merchant-side quick buy: pass an itemId, a per-unit price ceiling and an amount, and AssetPay buys the cheapest listings up to it. Available for CS2 and Rust.

Merchant-facing equivalent of [`POST /client/trading/withdraw/quick`](/docs/api-reference/trading/withdraw-quick). Instead of naming specific listings, you pass an `itemId`, a per-unit ceiling, and an amount — AssetPay buys the cheapest available listings at or below your ceiling and delivers them to the supplied `tradeUrl`. The merchant's USD wallet is debited.

**Available for CS2 and Rust.** Other games are rejected with `QUICK_BUY_UNSUPPORTED_GAME` (32). For Rust, `phase` and `delivery: "instant"` are not available (CS2-only options).

**Authentication:** Merchant API Key (`api-key` header)
**Scope:** `CORE_ACCESS`

## Request

```http theme={null}
POST https://api.assetpay.gg/secure/buy/quick
Content-Type: application/json
api-key: ap_...

{
  "tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=12345&token=ABCDEFGH",
  "itemId": "dc3c4460d814ac35",
  "maxPrice": 67.39,
  "amount": 3,
  "delivery": "instant",
  "externalId": "self_qb_001"
}
```

### Body Parameters

| Parameter        | Type   | Required | Description                                                                                                                                                                                                                                                                                                                        |
| ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tradeUrl`       | string | Yes      | Steam trade URL of the account receiving the items                                                                                                                                                                                                                                                                                 |
| `itemId`         | string | Yes\*    | Item id from `/secure/prices` (CS2 catalog id, or any listing id of the item for Rust). 5–256 chars. Provide this **or** `marketHashName`; `itemId` wins when both are sent.                                                                                                                                                       |
| `marketHashName` | string | Yes\*    | Exact market hash name (e.g. `AK-47 \| Redline (Field-Tested)`), resolved to the item. Ignored when `itemId` is supplied.                                                                                                                                                                                                          |
| `maxPrice`       | number | Yes      | **Gross** per-unit price ceiling in USD (fee-inclusive — same basis as the `withdraw` price in `/secure/prices`). Max \$100,000.                                                                                                                                                                                                   |
| `amount`         | number | Yes      | How many units to buy. Integer, 1–200.                                                                                                                                                                                                                                                                                             |
| `externalIds`    | array  | No       | One reference of your own per requested unit (each 1–128 chars), applied to the pre-filled rows in order. Must hold **exactly** `amount` entries. Accepted in place of the item id by both [cancel](/docs/api-reference/secure/cancel-buy-item) endpoints.                                                                              |
| `delivery`       | string | No       | `"standard"` or `"instant"`. Defaults to `"standard"`. `"instant"` is CS2-only.                                                                                                                                                                                                                                                    |
| `phase`          | string | No       | **CS2 only.** Doppler phase to buy: `"Phase 1"`–`"Phase 4"`, `"Ruby"`, `"Sapphire"`, `"Black Pearl"`, `"Emerald"`. Prices against that phase's floor for the requested delivery mode. Combines with `delivery: "instant"`, though many phases have no instant listing — check `phases[].withdraw.instant` on /secure/prices first. |
| `game`           | string | No       | `"730"` (CS2) or `"252490"` (Rust). Defaults to `"730"`.                                                                                                                                                                                                                                                                           |
| `autoCancel`     | number | No       | **CS2 only.** Minutes after creation to automatically cancel any unit still undelivered. Integer, 30-720. Defaults to `30`; send `720` to disable. Rejected on Rust with `VALIDATION_FAILED`. See [Automatic cancellation](/docs/guides/withdrawals#automatic-cancellation).                                                            |
| `externalId`     | string | No       | Your unique tracking ID                                                                                                                                                                                                                                                                                                            |

\* Provide either `itemId` or `marketHashName`.

## Response

Returns a full [`Trade`](/docs/reference/types#trade) object with `source: "self"`. As with the client quick withdrawal, `items` contains **one pre-filled row per requested unit** (carrying the ceiling `price` + `delivery`, same minimal shape as a standard buy item) and `totalPrice` is the **worst-case lock** (`amount × maxPrice`). Each row is bound to a real listing as fills arrive; unfilled rows go to `failed` and the unfilled / below-ceiling remainder is refunded — all asynchronously via the trade webhook. Once settled, `totalPrice` reflects the actual spend.

```json theme={null}
{
  "requestId": "...",
  "success": true,
  "data": {
    "id": "trade-uuid",
    "type": "withdraw",
    "source": "self",
    "status": "initiated",
    "game": "730",
    "externalId": "self_qb_001",
    "merchantId": "merchant-uuid",
    "clientSteamID": "76561198012345678",
    "clientTradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=12345&token=ABCDEFGH",
    "items": [
      { "id": "dc3c4460d814ac35", "appid": 730, "tradable": true, "amount": 1, "status": "initiated", "offer": { "price": 67.39, "delivery": "instant" } },
      { "id": "dc3c4460d814ac35", "appid": 730, "tradable": true, "amount": 1, "status": "initiated", "offer": { "price": 67.39, "delivery": "instant" } },
      { "id": "dc3c4460d814ac35", "appid": 730, "tradable": true, "amount": 1, "status": "initiated", "offer": { "price": 67.39, "delivery": "instant" } }
    ],
    "totalPrice": 202.17,
    "createdAt": "2026-05-31T12:00:00.000Z",
    "updatedAt": "2026-05-31T12:00:00.000Z"
  }
}
```

## How the wallet debit works

The merchant's available wallet balance must cover the worst-case lock (`amount × maxPrice`) at the time of the call. The funds are locked immediately; on each fill the sourcing portion settles to HOUSE and the AssetPay fee to HOUSE\_FEES, and the unfilled units plus any below-ceiling savings are released back to your balance.

## The `initiated` approval callback also fires

`/secure/buy/quick` reuses the same approval pipeline as the client quick withdrawal. After the trade is created (status `initiated`), AssetPay sends an [approval callback](/docs/guides/callbacks#the-initiated-callback-for-withdrawals) with `trade.source === "self"`. The trade does not proceed until you respond — note the callback carries the **pre-filled rows** (each at the ceiling price) and the worst-case `totalPrice`, since the actual fills aren't known yet.

* To auto-approve your own self-trades, return `2xx` whenever `trade.source === "self"`.
* To reject, respond with `4xx`; the trade is failed and the wallet lock is released.
* Self-trade quick buys still **require an active callback URL** — without one the call fails immediately with `MERCHANT_NO_CALLBACK_URL` (1705).

## Rate Limits

| Merchant Status | Limit                |
| --------------- | -------------------- |
| Verified        | 5,000 requests / min |
| Unverified      | 50 requests / min    |

## Errors

Same set as [`POST /client/trading/withdraw/quick`](/docs/api-reference/trading/withdraw-quick#errors). Common: `QUICK_BUY_UNSUPPORTED_GAME` (32, unsupported game), `MERCHANT_BALANCE_LOW` (12), `MERCHANT_NO_CALLBACK_URL` (1705), `INVALID_TRADEURL` (13), `INSUFFICIENT_STOCK` (42, Rust: nothing sourceable at or below `maxPrice`), `VALIDATION_FAILED` (1001).
