Appearance
Create Deposit by Hash Name
POST /api/v1/merchant/create-deposit-by-hash-name
Commits a deposit using a previously quoted trade ID from Get Prices. Unlike Create Deposit, this endpoint identifies items by market hash name plus the user's Steam asset_id — no inventory pre-fetch required.
This is step 2 of the hash-name integration flow.
Request
Headers
| Header | Required | Description |
|---|---|---|
X-Api-Key | ✅ | Your merchant API key |
Content-Type | ✅ | application/json |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | ✅ | Trade ID returned by get-prices. Min 1 |
merchant_tx_id | string | ✅ | Your unique transaction/order ID. Overwrites the temporary ID assigned at get-prices |
game | string | ✅ | Game code: csgo, rust, dota2, tf2 |
partner | integer | ✅ | Steam trade partner ID |
token | string | ✅ | Steam trade token (exactly 8 characters) |
items | object[] | ✅ | Items to deposit (min 1) |
items[].asset_id | string | — | Steam asset ID (numeric string — required in practice; validated as numeric) |
items[].name | string | ✅ | Market hash name — must match a name quoted in get-prices |
items[].min_price | number | — | Minimum acceptable price (USD). If the current price drops below this, the request fails. Values ≤ 0 are ignored |
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) |
Response
json
{
"success": true,
"message": "Deposit processing with selected items",
"data": {
"id": 742,
"merchant_tx_id": "order-9999",
"status": "active",
"amount": 45.50,
"bot_name": "Skinslink Bot #3",
"bot_steam_id": 76561199012345678,
"trade_offer_id": "6912345678",
"trade_offer_expiry_at": "2026-05-01T14:30:00Z"
}
}On provider failure, the response returns HTTP 200 with status: "failed" and a fail_reason:
json
{
"success": true,
"message": "Deposit processing with selected items",
"data": {
"id": 742,
"merchant_tx_id": "order-9999",
"status": "failed",
"amount": 45.50,
"bot_name": null,
"bot_steam_id": null,
"trade_offer_id": null,
"trade_offer_expiry_at": null,
"fail_reason": "provider_unavailable"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | integer | Internal trade ID (same as request) |
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 |
fail_reason | string | null | Present when status is failed. See Fail Reasons below |
INFO
Custom currency fields (custom_currency, custom_currency_multiplier, custom_currency_rate, custom_currency_sum) are not included in this response. Use Deposit Status once the trade reaches hold or completed status.
Processing Steps
- Validates the Steam trade key (partner + token).
- Loads the trade by
id, verifies it belongs to your merchant and is in statusnew. - Locks
merchant_tx_id— written along with game/partner/token/URLs in a DB transaction; the trade transitions topending. Duplicatemerchant_tx_idvalues are rejected here. - Checks the 5-minute price window — if the original
get-pricescall was more than 5 minutes ago, the request fails withinventory_reloadand you must callget-pricesagain. - Recalculates resale margin using the inventory data cached at
get-pricestime. - Per-item
min_pricecheck — if any item's current price is below the suppliedmin_price, the request fails withitem_specified_price_not_found. - Inserts trade items into the DB.
- Dispatches to provider (10-second timeout, detached from the client connection).
- On provider failure → trade set to
failed,fail_reason: provider_unavailable. - On success → trade set to
activeand the response is returned.
5-minute price expiry
Prices quoted by get-prices expire after 5 minutes. After that, you must call get-prices again to obtain a fresh id.
min_price
min_price protects you from price drift inside the 5-minute window. Set it slightly below the price you displayed to the user (e.g. quoted price minus 1–2%). If you don't supply min_price, the deposit will go through at whatever the current price is.
Example
bash
curl -X POST https://api.skinslink.com/api/v1/merchant/create-deposit-by-hash-name \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"id": 742,
"merchant_tx_id": "order-9999",
"game": "csgo",
"partner": 123456789,
"token": "AbCdEfGh",
"items": [
{
"asset_id": "38029384123",
"name": "AK-47 | Redline (Field-Tested)",
"min_price": 20.00
}
]
}'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 Deposit Status for updates.
TIP
Store 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). Includes inventory_reload (price quote expired) and item_specified_price_not_found (item below min_price) |
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 merchant's allowed list |
400 | validation error | Trade id is not in new status — already activated or expired. Check data[].code for status: denied |
404 | not found error | Trade id not found or does not belong to this merchant |
409 | already exist error | Duplicate merchant_tx_id |
500 | internal error | Something went wrong on our side |
Fail Reasons
Some failures return HTTP 200 with status: "failed" in the response body; others return an HTTP error directly. All are persisted on the trade and visible via Deposit Status.
In-band (HTTP 200, status: "failed"):
| Fail Reason | Meaning |
|---|---|
provider_unavailable | Provider rejected the trade, timed out (10s), or an internal error occurred before dispatch (e.g. DB failure saving trade items) |
Out-of-band (HTTP 400 error response):
These are returned immediately in the HTTP 400 response body. The trade is also persisted with the corresponding fail_reason, so it is additionally visible via Deposit Status.
| Fail Reason | HTTP body format | Meaning |
|---|---|---|
inventory_reload | validation error with data[{field: "inventory", code: "inventory_reload"}] | Price quote expired (> 5 minutes since get-prices), or no provider was selected |
item_specified_price_not_found | message: "item_specified_price_not_found" | An item's current price dropped below its min_price |
→ For domain-specific validation codes, see Errors.
