# SkinsNode API Reference Token estimate: about 2,400 tokens. SkinsNode is a CS2 skin fulfillment API. Use it to read a live account-scoped catalog, create asynchronous customer skin orders, and track delivery through signed webhooks or polling. Base URL: `https://api.skinsnode.com` ## Authentication Authenticated routes require two headers: ```http x-client-id: x-client-secret: ``` Keep credentials server-side. Do not expose `x-client-secret` in browser code, mobile apps, logs, analytics, or public repositories. ## Endpoints | Method | Path | Auth | Cache behavior | Purpose | | --- | --- | --- | --- | --- | | GET | `/v1/health` | No auth | No cache | Check API liveness. | | GET | `/v1/wallet` | Client credentials | Cached up to 30 seconds | Return the authenticated prepaid balance. | | GET | `/v1/catalog` | Client credentials | Account scoped | Return currently available CS2 skin items. | | POST | `/v1/orders` | Client credentials | No create cache | Accept an order for asynchronous processing. | | GET | `/v1/orders/{externalId}` | Client credentials | Cached up to 30 seconds | Return latest order state. | ## GET /v1/health Use this endpoint to verify API liveness. It does not require authentication. Response: ```json { "status": "ok", "service": "skinsnode" } ``` ## GET /v1/wallet Use this endpoint to read the authenticated client's prepaid balance. Response: ```json { "clientId": "client_live_abc123", "currency": "USD", "availableBalance": "125000" } ``` `availableBalance` is an integer string in cents. ## GET /v1/catalog Use this endpoint before checkout to get current item availability and prices. Response: ```json { "items": [ { "id": "8d15e82b-21fe-4ec1-a37f-1e2e5edeb1fd", "marketHashName": "Example Item", "price": 1840, "currency": "USD", "phase": null, "floatValue": "0.2183", "paintIndex": 282, "paintSeed": 771, "rarityColor": "#eb4b4b", "classId": "310777746", "instanceId": "188530139", "imageUrl": "https://..." } ] } ``` Use the item `id` as `catalogItemId` when creating an order. `price` is integer cents. ## POST /v1/orders Use this endpoint to submit a customer order for asynchronous processing. A successful response is `202 Accepted`. Request body: ```json { "catalogItemId": "8d15e82b-21fe-4ec1-a37f-1e2e5edeb1fd", "expectedPrice": 1840, "externalId": "order_100042", "webhookUrl": "https://store.example/webhooks/orders", "tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc" } ``` Response: ```json { "externalId": "order_100042", "status": "accepted", "acceptedAt": "2026-04-25T12:30:00.000Z" } ``` Use the store application's stable order ID as `externalId`. Treat `externalId` as the idempotency key. If the same client sends the same `externalId` again, the API returns the existing accepted order instead of creating a duplicate. ## GET /v1/orders/{externalId} Use this endpoint as a polling fallback when webhook delivery is delayed or unavailable. Response: ```json { "externalId": "order_100042", "status": "completed", "steamOfferId": "7193849201", "tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc", "item": { "id": "8d15e82b-21fe-4ec1-a37f-1e2e5edeb1fd", "marketHashName": "Example Item", "price": 1840, "currency": "USD" }, "pricing": { "currency": "USD", "amount": "1840" }, "failure": null, "timestamps": { "queuedAt": "2026-04-25T12:30:00.000Z", "updatedAt": "2026-04-25T12:36:12.000Z", "refundedAt": null } } ``` ## Recommended Checkout Flow 1. Refresh `GET /v1/catalog` before showing or quoting items. 2. Confirm the selected item still exists and use its `id` as `catalogItemId`. 3. Collect or retrieve the recipient Steam trade URL. 4. Submit `POST /v1/orders` with `catalogItemId`, `expectedPrice`, `externalId`, `webhookUrl`, and `tradeUrl`. 5. Store the accepted order locally. 6. Prefer signed webhooks for status updates. 7. Poll `GET /v1/orders/{externalId}` until a terminal status is returned if webhooks are delayed. ## Order Statuses | Status | Terminal | Meaning | | --- | --- | --- | | `queued` | No | Waiting for background processing. | | `processing` | No | Worker is actively processing the order. | | `accepted` | No | Create request was accepted for asynchronous processing. | | `pending` | No | Delivery flow has started. | | `sent` | No | Delivery is waiting for recipient action or a later status update. | | `completed` | Yes | Order finished successfully. | | `failed` | Yes | Order failed before completion. | | `cancelled` | Yes | Order was cancelled before completion. | | `expired` | Yes | Recipient did not complete the delivery in time. | | `reversed` | Yes | Completed delivery was later reversed. | ## Webhooks The API sends `order.updated` events to the submitted `webhookUrl`. Webhook headers: ```http x-event-type: order.updated x-delivery-id: 01971d02-... x-timestamp: 2026-04-25T12:36:12.000Z x-signature: ``` Verify `x-signature` by computing an HMAC-SHA256 hex digest over: ```text . ``` Use the same client secret sent as `x-client-secret` as the HMAC key. Always verify the signature before trusting the payload. ## Error Handling | HTTP code | Meaning | | --- | --- | | `401` | Missing, inactive, or invalid client credentials. | | `403` | Source IP is not allowed for this client. | | `404` | Catalog item or order was not found. | | `422` | Request body, params, or query failed validation. | Known failure codes include `item_unavailable`, `price_changed`, `insufficient_funds`, and `cancelled`.