Appearance
Create Deposit
POST /api/v1/merchant/create-deposit
Creates a deposit with specific Steam items. Used in the API-only integration flow after fetching inventory.
Request
Headers
| Header | Required | Description |
|---|---|---|
X-Api-Key | ✅ | Your merchant API key |
Content-Type | ✅ | application/json |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
merchant_tx_id | string | ✅ | Your unique transaction/order ID |
game | string | ✅ | Game code: csgo, rust, dota2, tf2 |
partner | integer | ✅ | Steam trade partner ID |
token | string | ✅ | Steam trade token |
asset_ids | string[] | ✅ | Item IDs from the inventory response. Must not exceed max_items (see Get Inventory) |
result_url | string | — | Webhook URL — overrides your merchant-level webhook URL for this deposit (must be valid URL) |
success_url | string | — | Success redirect URL (must be valid URL) |
fail_url | string | — | Failure redirect URL (must be valid URL) |
custom_currency | string | — | Custom currency code (1–4 chars, e.g. BTC, EUR). Requires all 3 currency fields |
custom_currency_multiplier | number | — | Currency multiplier (0.5–2.0) |
custom_currency_rate | number | — | Currency exchange rate (≥ 0.1) |
min_prices | object | — | Per-item price floor in USD, keyed by asset_id (e.g. {"38029384123": 19.50}). If an item's price at deposit time is below its floor, the deposit is rejected with item_specified_price_not_found and no trade is created. Entries ≤ 0 are ignored |
from_preview | boolean | — | Credit the deposit at the prices shown by Deposit Preview. Defaults to false. Requires preview access on your account; without it the flag is ignored and the deposit runs the regular flow. See Deposit at Preview Prices |
Pricing & Price Lock
The deposit amount is derived from the inventory snapshot of your most recent Get Inventory call for the same Steam account (partner + token). That snapshot is cached for 5 minutes.
- If you call
create-depositmore than 5 minutes after fetching the inventory, the request fails withinventory_reload— re-fetch the inventory and retry. - Without
min_prices, the creditedamountis the price cached at deposit time, not the moment you displayed it to the user.
Protect against price drift with min_prices
Pass the optional min_prices map (keyed by asset_id, in USD) to set a per-item floor. If any item's price at deposit time is below its floor, the deposit is rejected with item_specified_price_not_found and no trade is created — so you are never credited less than you displayed. Set each floor slightly below the price you showed the user (e.g. quoted minus 1–2%). Prices are USD regardless of any custom_currency fields.
Alternative: hash-name flow
Get Prices → Create Deposit by Hash Name offers the same per-item min_price protection plus a per-transaction quote snapshot (by id), so the quote cannot be overwritten by a concurrent inventory fetch.
Deposit at Preview Prices (from_preview)
Access on request
This flow is enabled per merchant account, together with Deposit Preview itself. Until we enable it for yours, from_preview is ignored: the deposit is still created, but through the regular flow described above — including its 5-minute Get Inventory snapshot rule. Ask your account manager to turn it on.
Set from_preview: true to credit the deposit at the prices Deposit Preview shows — the flow for carts built from the preview:
- Call Deposit Preview and show the user our prices.
- The user picks items ("the cart").
- Call
create-depositwith the pickedasset_idsandfrom_preview: true.
The credited amount equals the current preview pricing of exactly those items — if a price has updated since the user saw it, the current price is credited. No prior Get Inventory call is required, and the 5-minute inventory_reload snapshot rule does not apply.
Rejections
Both rejections happen before anything is created: no deposit record, no webhook, and your merchant_tx_id is not consumed — simply retry with the same ID.
price_changed — the cart cannot be credited as it stands: an item can no longer be accepted, or we cannot accept the cart at these prices right now. data carries the current pricing of the same cart, in the Deposit Preview response shape, and data.rejected names every item that blocks it — re-show these prices, drop the rejected items, and retry. No extra preview call is needed:
json
{
"success": false,
"message": "price_changed",
"data": {
"items": [
{ "id": "38029384123", "name": "AK-47 | Redline (Field-Tested)", "price": 12.10 }
],
"rejected": [
{ "id": "51819053033", "reason": "not_in_inventory" }
],
"total": 1,
"sum": 12.10,
"game": "csgo",
"max_items": 50
}
}rejected[].reason | Meaning |
|---|---|
not_in_inventory | The asset ID is not in the user's inventory — drop it from the cart |
not_quoted | We do not accept this item right now — drop it from the cart |
data.items lists what we would still credit, with current prices — items in rejected are not part of it.
preview_unavailable — preview pricing is temporarily unavailable. Retry later, or fall back to the regular Get Inventory → create-deposit flow.
from_preview vs min_prices
min_prices protects a regular deposit against price drift with a floor you compute yourself. from_preview needs no floor: the cart is credited at our current preview prices.
Response
json
{
"success": true,
"message": "Deposit processing with selected items",
"data": {
"id": 42,
"merchant_tx_id": "order-12345",
"status": "active",
"amount": 36.25,
"bot_name": "Skinslink Bot #3",
"bot_steam_id": 76561199012345678,
"trade_offer_id": "6912345678",
"trade_offer_expiry_at": "2026-02-16T12:30:00Z",
"success_url": "https://yoursite.com/deposit/success",
"fail_url": "https://yoursite.com/deposit/failed"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | integer | Internal trade ID |
merchant_tx_id | string | Your transaction ID (echoed back) |
status | string | Trade status (active on success, failed on error) |
amount | number | Total deposit amount (USD) |
bot_name | string | null | Name of the Steam bot sending the offer |
bot_steam_id | integer | null | Steam ID of the bot |
trade_offer_id | string | null | Steam trade offer ID |
trade_offer_expiry_at | string | null | ISO 8601 timestamp when the offer expires |
success_url | string | Success redirect URL (only present if provided in request) |
fail_url | string | Failure redirect URL (only present if provided in request) |
INFO
Custom currency fields (custom_currency, custom_currency_multiplier, custom_currency_rate, custom_currency_sum) are not included in the create-deposit response. Use Deposit Status to retrieve them once the trade reaches hold or completed status.
Example
bash
curl -X POST https://api.skinslink.com/api/v1/merchant/create-deposit \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"merchant_tx_id": "order-12345",
"game": "csgo",
"partner": 123456789,
"token": "AbCdEfGh",
"asset_ids": ["38029384123", "38029384124"]
}'To add a per-item price floor, include min_prices in the request body:
json
{
"merchant_tx_id": "order-12346",
"game": "csgo",
"partner": 123456789,
"token": "AbCdEfGh",
"asset_ids": ["38029384123", "38029384124"],
"min_prices": {
"38029384123": 19.50,
"38029384124": 4.20
}
}After Creating a Deposit
- Show the trade offer info to the user (bot name, offer ID)
- User accepts the trade offer in Steam
- Listen for webhooks or poll
/merchant/deposit/statusfor updates
TIP
Store the trade_offer_expiry_at and remind users to accept before expiry. Expired offers are automatically canceled.
Errors
| Status | Message | Cause |
|---|---|---|
400 | invalid request body | Malformed JSON |
400 | validation error | Missing or invalid fields (see data array for details). Includes inventory_reload — the inventory price snapshot expired (> 5 min since Get Inventory); re-fetch and retry |
400 | item_specified_price_not_found | An item's price at deposit time dropped below its min_prices floor. No trade is created; re-fetch the inventory for current prices |
400 | deposit exceeds the maximum of N items per deposit | asset_ids has more items than the per-deposit limit — read max_items from Get Inventory. No trade is created; split into smaller deposits |
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 |
404 | not found error | Trade or items not found |
409 | already exist error | Duplicate merchant_tx_id |
500 | internal error | Something went wrong on our side |
→ For domain-specific validation codes (Steam account issues, trade eligibility), see Errors.
