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

# Buy Items (Self-Trade)

> Merchant-side withdrawal: AssetPay sources listings from the upstream marketplace and delivers them to the Steam account behind the supplied trade URL.

Merchant-facing equivalent of [`POST /client/trading/withdraw`](/docs/api-reference/trading/withdraw). AssetPay sources the listings from the upstream marketplace and delivers them to the supplied `tradeUrl`. The merchant's USD wallet is debited.

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

## Request

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

{
  "tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=12345&token=ABCDEFGH",
  "items": [
    { "itemId": "e5f6g7h8-...", "price": 45.00 }
  ],
  "game": "730",
  "externalId": "self_buy_001"
}
```

### Body Parameters

| Parameter            | Type   | Required | Description                                                                                                                                                                                                                                                             |
| -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tradeUrl`           | string | Yes      | Steam trade URL of the account receiving the items                                                                                                                                                                                                                      |
| `items`              | array  | Yes      | Items to buy (min 1, max 50)                                                                                                                                                                                                                                            |
| `items[].itemId`     | string | Yes      | Specific listing ID from `/secure/market/item`                                                                                                                                                                                                                          |
| `items[].price`      | number | Yes      | Purchase price in USD (max \$100,000). Must equal the current listing price.                                                                                                                                                                                            |
| `items[].externalId` | string | No       | Your own reference for this single item (1–128 chars). Echoed back on the item and accepted in place of the item id by both [cancel](/docs/api-reference/secure/cancel-buy-item) endpoints.                                                                                  |
| `game`               | string | No       | `"730"` or `"252490"`. Defaults to `"730"`.                                                                                                                                                                                                                             |
| `autoCancel`         | number | No       | **CS2 only.** Minutes after creation to automatically cancel any item 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                                                                                                                                                                                                                                                 |

## Response

Returns a full [`Trade`](/docs/reference/types#trade) object with `source: "self"`. On `initiated`, each item only carries the data you submitted (`itemId`, `offer.price`) — Steam-side fields like `name`, `marketHashName`, `type`, and `iconUrl` are **omitted** until they resolve.

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

## How the wallet debit works

The merchant's available wallet balance must cover `totalPrice` at the time of the call. The funds are locked immediately; on failure they're released, on success the sourcing portion settles to HOUSE and the AssetPay fee to HOUSE\_FEES.

## The `initiated` approval callback also fires

`/secure/buy` reuses the same approval pipeline as the client withdraw flow. After the trade is created (status `initiated`), AssetPay sends an [approval callback](/docs/guides/callbacks#the-initiated-callback-for-withdrawals) to your registered callback URL with `trade.source === "self"`. The trade does not proceed until you respond.

* To auto-approve all your own self-trades, return `2xx` whenever `trade.source === "self"` in your handler.
* To reject (e.g. dry-run mode or operational kill-switch), respond with `4xx` and the trade is failed and the wallet lock is released.
* Self-trade buys still **require an active callback URL** on the merchant account — calling this endpoint without one fails immediately with `MERCHANT_NO_CALLBACK_URL` (1705).

<Warning>
  This is different from `/secure/sell`, where there is no merchant-approval step at all. For `/secure/buy`, your callback handler must explicitly approve self-trades — there is no built-in bypass.
</Warning>

## Rate Limits

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

## Errors

Same set as [`POST /client/trading/withdraw`](/docs/api-reference/trading/withdraw#errors). Common: `MERCHANT_BALANCE_LOW` (12), `MERCHANT_NO_CALLBACK_URL` (1705), `LISTING_NOT_FOUND` (14), `PRICE_CHANGED` (11), `INVALID_TRADEURL` (13).
