# CS2 Skin Deposits API Accept CS2 skin deposits on your website or app. SkinsNode prices the user's inventory, receives the deposited skins in a Steam trade and credits your SkinsNode client account. Confirm endpoint access and commercial terms for your account during onboarding. The catalog and purchase-order API uses a separate, unchanged contract. ## How deposits work Your application requests deposit prices for a Steam user's inventory. The user selects eligible skins and deposits them through a Steam trade, and the agreed USD amount is credited to **your SkinsNode API client account**. That balance can fund subsequent skin purchase orders. This is not a bank, crypto or cash payout to the user. Any user-facing reward or payout is your application's separate responsibility. Receiving items is not the same as a final, irreversible settlement. The default contract records pending credit during Steam protection and makes it available only after settlement is verified. Early, spendable credit is not included in this flow. ## Two directions, one account | Flow | Items | SkinsNode client balance | | --- | --- | --- | | Withdrawals | Inventory to your customer's Steam account | Existing order debit rules | | Deposits | User's Steam inventory to SkinsNode | Pending credit, then available credit after settlement | Purchase fulfillment uses `GET /v1/catalog`, `POST /v1/orders` and `order.updated`. Your application does not need bot API credentials or Steam secrets. ## Endpoints The API origin is `https://api.skinsnode.com`. All calls are server-to-server with `x-client-id` and `x-client-secret`. No new domain or browser authentication flow is needed. [Download the OpenAPI schema](/deposits.openapi.json). Use the endpoint configuration confirmed during onboarding rather than assuming access from a schema download. | Method | Path | Result | | --- | --- | --- | | POST | `/v1/deposits/quotes` | 202, durable quote request | | GET | `/v1/deposits/quotes/{quoteId}` | Pending, ready or failed inventory quote | | POST | `/v1/deposits/offers` | 202, durable deposit of the selected quoted skins | | GET | `/v1/deposits/offers/{externalId}` | Transaction, Steam offer and account-credit state | Both POST routes require `Idempotency-Key: `. Keys are scoped to authenticated client and operation. The same key and same payload returns the same resource; different data returns `409 IDEMPOTENCY_CONFLICT`. An `externalId` uniquely identifies an offer for the authenticated client, including when a caller accidentally supplies a new key. Never create a second operation to recover from an HTTP timeout. ### 1. Request inventory valuation ```json { "partnerSteamId": "76561198000000001", "tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=39734273&token=abcd1234" } ``` These are illustrative values. Steam IDs and asset IDs are decimal strings. The Steam account and trade URL must match. The client account to credit is derived from authenticated credentials, never supplied in the body. A trade URL does not prove the requester owns the Steam account; your application must authenticate the user and obtain consent before requesting an offer. The first response contains a `quoteId`, `status: pending`, no items and no expiry. Poll the quote at most once every five seconds with backoff; honor `Retry-After`. A ready quote contains `expiresAt` and items with persistent `inventoryAssetId`, quote-scoped `itemId`, `marketHashName`, `phase`, `sellable`, `creditAmountCents` and `unavailableReason`. All public money values are **integer USD cents**: `3991` means USD 39.91. `creditAmountCents` is the final amount credited to the API client for that item, after the quoted commercial adjustments. No extra fee is silently added at acceptance. Unknown, stale or unsupported valuations return `sellable: false`, a null amount and a reason. A zero is not a valid purchase quote. Inventory pages must be fully collected and deduplicated before a ready quote is published; no partial inventory may masquerade as a complete result. Valuation uses sale-side evidence, not ask/listing prices. Catalog estimates do not include unverified premiums for float, paint seed, stickers or charms. Preserve wear, StatTrak and phase boundaries. Expired quotes must be refreshed and accepted again. Version 1 accepts one unit per asset and at most 100 unique items in one offer. ### 2. Select items and accept the account-credit amount ```json { "externalId": "deposit_42", "quoteId": "019e0000-0000-7000-8000-000000000001", "items": [{ "itemId": "019e0000-0000-7000-8000-000000000002" }], "expectedCreditAmountCents": 3991, "webhookUrl": "https://merchant.example/webhooks/skinsnode" } ``` The server checks quote ownership, expiry, selected item eligibility, the sum of selected credits, current item availability and the original recipient identity. It reserves those exact assets before submission. Price changes require a new quote, not silent repricing. Version 1 creates one trade on one selected bot, all-or-nothing; partial or multi-bot sessions are out of scope. `202` does not mean accepted by Steam or credited to your balance. Never request a replacement while outcome is uncertain. ### 3. Track the trade and balance credit | Transaction status | Credit status | Meaning | | --- | --- | --- | | `new`, `verifying`, `pending` | `not_credited` | Validating, submitting or waiting for the user's Steam acceptance | | `on_hold` | `pending` | Items received; trade protection or settlement verification still pending | | `success` | `available` | Verified settlement and one committed client-wallet credit | | `canceled`, `failed` | `not_credited` | Definitive pre-settlement failure, no credit | | `reverted` | `reversed` | Steam reversed the trade; pending credit canceled or ledger credit compensated | `holdReleaseAt` is nullable and informational. Do not release money based on a timer alone. `walletTransactionId` is mandatory on success and identifies the actual ledger entry. The transaction credit total equals the sum of its selected quoted items. An unknown send/cancel/settlement outcome remains in reconciliation, not failure. Duplicate events cannot add credit twice. Reversal after credit is represented by a compensating ledger entry, never deleting history or silently clamping a balance to zero. ### Webhooks and recovery The `deposit.updated` event contains `version: 1`, UUID `eventId`, ISO `timestamp` and a full transaction snapshot in `data`, including a monotonic `revision`. Verify `x-signature` as hex HMAC-SHA256 over `x-timestamp + "." + rawBody` with the client webhook secret. The header timestamp is Unix milliseconds; require a five-minute window and constant-time signature comparison. Persist the event in a durable inbox before returning 2xx. Deduplicate by event ID and ignore older snapshot revisions. Delivery retries keep the event ID and use a fresh signed header timestamp. Poll the tenant-scoped offer endpoint as a bounded recovery path. Do not expose credentials or internal bot callbacks in the browser. Backend implementations must validate callback destinations against SSRF at registration and delivery, reject private/reserved addresses and redirects, and avoid logging trade tokens or secrets. ## Access and errors Commercial terms are agreed during onboarding. This is client-account credit, not an immediate cash payout. Errors include `QUOTE_EXPIRED`, `PRICE_CHANGED`, `ITEMS_UNAVAILABLE`, `INVENTORY_PRIVATE`, `QUOTE_UNAVAILABLE`, `IDEMPOTENCY_CONFLICT` and `RATE_LIMITED`. Endpoint access depends on account configuration. A disabled capability can return `503 FEATURE_NOT_AVAILABLE`; contact support instead of repeatedly retrying. [Contact SkinsNode](https://t.me/tek9nino) to discuss integration and commercial terms. ## Contract validation The published JSON schema describes shapes. Additional normative rules above include cross-field identity validation, unique selected assets, quote expiry, matching totals, status-to-credit consistency, tenant isolation and durable settlement. Generated clients must not treat shape validation as authorization, a quote guarantee or payment proof.