Appearance
Create Purchase
POST /api/v1/merchant/purchase
Creates a skin purchase. Skinslink sends a trade offer with the requested item to the specified Steam account.
Request
Headers
| Header | Required | Description |
|---|---|---|
X-Api-Key | ✅ | Your merchant API key |
Content-Type | ✅ | application/json |
Accept | — | application/json |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
partner | integer | ✅ | Steam trade partner ID (minimum: 8) |
token | string | ✅ | Steam trade token (exactly 8 characters) |
game | string | ✅* | Game code: csgo, rust, dota2, tf2 |
name | string | ✅* | Item market name |
asset_id | string | ✅* | Catalogue item ID (items[].id from Available Items or Search Items) to buy exactly that item. Not a Steam asset ID, see Available Items |
merchant_tx_id | string | — | Your unique transaction/order ID. Also the idempotency key — see Idempotency |
max_price | number | — | Maximum price you're willing to pay (USD). Inclusive: an item priced exactly at max_price is bought. Use at most 2 decimal places |
* Provide either (game + name) or asset_id (game is optional then).
Idempotency
merchant_tx_id is your idempotency key. If a purchase with that id already exists for your account, we return that purchase instead of creating a second one — the same response shape, carrying the purchase's current status.
Retries are therefore safe: repeating a request after a timeout, a dropped connection, or any answer you are unsure about can never create a duplicate purchase or charge you twice.
Requests sent without merchant_tx_id are not deduplicated — each one creates a new purchase.
WARNING
Use a fresh merchant_tx_id for every new order. Reusing the id of an earlier order returns that earlier order, even when the request body now asks for a different item.
Response
json
{
"success": true,
"message": "Purchase created successfully",
"data": {
"id": 178,
"merchant_tx_id": "order-12345",
"status": "pending",
"steam_id": "76561198338314767",
"date": "2026-03-24T10:30:00Z",
"amount": 45.99,
"item": {
"id": "38029384123",
"name": "AK-47 | Redline (Field-Tested)",
"price": 45.99,
"image_url": "https://steamcommunity-a.akamaihd.net/economy/image/class/730/..."
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | integer | Purchase ID |
merchant_tx_id | string | Your transaction ID (only present if provided in request) |
status | string | Purchase status (see Purchase Status) |
fail_reason | string | Failure reason (present when status is failed, canceled or reverted, see Fail Reasons) |
steam_id | string | User's Steam ID (64-bit, as string) |
steam_name | string | User's Steam display name (only present if available) |
steam_avatar_url | string | User's Steam avatar URL (only present if available) |
amount | number | Purchase amount (USD) |
trade_offer_id | string | Steam trade offer ID (only present if available) |
date | string | Purchase creation timestamp (ISO 8601) |
item | object | Purchased item details (only present if available) |
item.id | string | Catalogue item ID, see Available Items |
item.name | string | Item market name |
item.price | number | Item price (USD) |
item.image_url | string | Steam CDN image URL |
INFO
Optional fields (merchant_tx_id, fail_reason, steam_name, steam_avatar_url, trade_offer_id, item) are omitted from the response when not available — they will not appear as null.
Fail Reasons
| Reason | Description |
|---|---|
insufficient_balance | Not enough balance in your merchant account |
price_changed | The item's price moved since your catalogue lookup, including above your max_price |
item_sold | The specific item was sold or withdrawn after your catalogue snapshot — refresh the catalogue and buy another listing |
item_not_available | No matching item is currently available to buy |
duplicate_purchase | The same item is already held by another of your in-progress purchases |
item_specified_price_not_found | No item found within the specified max_price |
service_unavailable | Service temporarily unavailable |
Example
bash
curl -X POST https://api.skinslink.com/api/v1/merchant/purchase \
-H "X-Api-Key: your-api-key" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"partner": 123456789,
"token": "AbCdEfGh",
"game": "csgo",
"name": "AK-47 | Redline (Field-Tested)",
"merchant_tx_id": "order-12345"
}'After Creating a Purchase
- Track the purchase status via webhooks or poll Purchase Status
- User accepts the trade offer in Steam
- Purchase moves to
completedonce the trade is finalized
TIP
If the purchase fails immediately (e.g., insufficient_balance), the response will have status: "failed" with the fail_reason field populated.
Errors
| Status | Message | Cause |
|---|---|---|
400 | invalid request body | Malformed JSON |
400 | validation error | Missing or invalid fields (see data array for details) |
401 | missing authorization header | X-Api-Key header not provided |
401 | invalid API key | API key not found or inactive |
403 | merchant account is disabled | Merchant account deactivated |
403 | IP not whitelisted: <ip> | Request IP not in whitelist |
500 | internal error | Something went wrong on our side |
For trade link and Steam account validation codes, see Purchase Errors.
