Appearance
Webhooks
Skinslink sends webhook notifications to your server when trade statuses change. This is the recommended way to track deposit progress.
Setup
Configure your webhook URL in your merchant settings. Skinslink will send POST requests to this URL whenever a trade status changes.
You can also override the webhook URL per deposit by passing result_url when creating a deposit — that URL will take priority over the merchant-level setting.
Payload
json
{
"sign": "b64EncodedSHA256Signature==",
"status": "completed",
"trade_id": 178,
"trade_date": "2026-02-16T12:00:00Z",
"merchant_tx_id": "order-12345",
"steam_id": "76561198338314767",
"offer_id": "6912345678",
"amount": 36.25,
"amount_currency": "usd",
"custom_currency": "EUR",
"custom_currency_multiplier": 1.0,
"custom_currency_rate": 0.92,
"custom_currency_sum": 33.35
}Fields
| Field | Type | Description |
|---|---|---|
sign | string | Request signature (see Verifying Webhooks below) |
status | string | Trade status: hold, canceled, failed, completed, reverted |
trade_id | integer | Skinslink internal trade ID |
trade_date | string | Trade creation timestamp (ISO 8601) |
merchant_tx_id | string | Your order ID (the value you passed when creating the deposit) |
steam_id | string | User's Steam ID (64-bit, as string) |
offer_id | string | Steam trade offer ID (empty string if not yet available) |
amount | number | Trade amount in USD |
amount_currency | string | Currency code (currently always usd) |
custom_currency | string | null | Custom currency code (only for hold/completed) |
custom_currency_multiplier | number | null | Currency multiplier |
custom_currency_rate | number | null | Currency exchange rate |
custom_currency_sum | number | null | Computed amount in custom currency (amount × rate × multiplier) |
Trade Status Flow
Webhooks are sent for the highlighted statuses only:
new ──→ pending ──→ active ──→ completed ✉
│ │ │
│ │ ├──→ hold ✉ ──→ completed ✉
│ │ │ │
│ │ │ └──→ reverted ✉
│ │ │
│ │ ├──→ failed ✉
│ │ │
│ │ └──→ canceled ✉
│ │
│ ├──→ failed ✉
│ │
│ └──→ canceled ✉
│
└──→ canceled ✉- hold — trade is on Steam trade hold (7-day hold)
- completed — items received, deposit credited
- failed — trade failed (user declined, items unavailable, etc.)
- canceled — trade was canceled
- reverted — a previously held trade has been reversed
TIP
Statuses new, pending, and active do not trigger webhook notifications. Use polling if you need to track early status transitions.
Verifying Webhooks
Why verify?
Your webhook endpoint is a public URL — anyone who discovers it could send fake requests pretending to be Skinslink (e.g. "deposit completed, credit the user $50"). The sign field lets you cryptographically prove the webhook actually came from us and hasn't been tampered with.
Always verify the signature before processing a webhook. Without verification, an attacker could forge deposit completions and steal funds.
How it works
The signature is a Base64-encoded SHA-256 hash of your merchant_tx_id concatenated with your merchant secret:
sign = base64( sha256( merchant_tx_id + secret ) )Your secret is known only to you and Skinslink — so only a legitimate webhook can produce the correct signature for a given merchant_tx_id.
Example (Python)
python
import hashlib, base64
expected = base64.b64encode(
hashlib.sha256((payload["merchant_tx_id"] + secret).encode()).digest()
).decode()
if expected != payload["sign"]:
return 403 # reject — not from SkinslinkExample (Node.js)
javascript
const crypto = require('crypto');
const expected = crypto
.createHash('sha256')
.update(payload.merchant_tx_id + secret)
.digest('base64');
if (expected !== payload.sign) {
return res.status(403).send('Invalid signature');
}Retry Behavior
If your webhook endpoint returns a non-2xx status code, Skinslink will retry delivery with exponential backoff. We recommend:
- Respond with
200 OKas quickly as possible - Process the webhook payload asynchronously
- Implement idempotency using
merchant_tx_id+statusas the deduplication key
Polling as Fallback
If webhooks are not suitable for your setup, you can poll the deposit status endpoint:
bash
curl "https://api.skinslink.com/api/v1/merchant/deposit/status?merchant_tx_id=order-12345" \
-H "X-Api-Key: your-api-key"We recommend polling no more than once per second per deposit.
