# CS2 Skin Fulfillment API Integration Prompt for AI Agents Use this prompt when an AI coding agent needs to integrate an application that sells CS2 skins through the SkinsNode fulfillment API. ## Prompt You are integrating an application with this CS2 skin fulfillment API to sell CS2 skins, create customer skin orders, and track order status. Base URL: `` Implement the integration server-side. Never expose `x-client-secret` in browser code, mobile apps, logs, analytics, or public repositories. Store credentials in environment variables or a secret manager. Required client headers for authenticated routes: ```http x-client-id: x-client-secret: ``` Available endpoints: ```http GET /v1/health GET /v1/wallet GET /v1/catalog POST /v1/orders GET /v1/orders/{externalId} ``` Endpoint behavior: - `GET /v1/health` needs no authentication and checks API liveness. - `GET /v1/wallet` returns the authenticated prepaid balance. - `GET /v1/catalog` returns items available to the authenticated account. - `POST /v1/orders` accepts an order for asynchronous processing and returns HTTP `202`. - `GET /v1/orders/{externalId}` returns the latest known state for one order. Money fields: - Catalog `price` is integer cents. - Wallet `availableBalance` is an integer string. - Order `pricing.amount` is an integer string. - Trade acceptance window is 600 seconds (10 minutes) by default. - Currency is currently returned as `USD`. Order creation 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" } ``` Order creation response: ```json { "externalId": "order_100042", "status": "accepted", "acceptedAt": "2026-04-25T12:30:00.000Z" } ``` Use the store application's own 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. Recommended checkout flow: 1. Refresh `GET /v1/catalog` before showing or quoting an item. 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. Keep the buyer aware that trade acceptance must happen within 10 minutes. 6. Store the accepted order locally. 7. Prefer signed webhooks for status updates. 8. Use `GET /v1/orders/{externalId}` as a polling fallback until a terminal status is returned. Order statuses: - `queued`: waiting for background processing. - `processing`: actively being processed. - `accepted`: create request accepted for asynchronous processing. - `pending`: delivery flow has started. - `sent`: delivery is waiting for recipient action or a later status update. - `completed`: successful terminal status. - `failed`: failed terminal status. - `cancelled`: cancelled terminal status. - `expired`: expired terminal status. - `reversed`: completed delivery was later reversed; terminal status. Terminal statuses are `completed`, `failed`, `cancelled`, `expired`, and `reversed`. Order lookup responses include: - `externalId` - `status` - `steamOfferId` - `tradeUrl` - `item` snapshot - `pricing` - `failure` - `timestamps` For successful orders, `failure` is `null`. For failed orders, `failure` is an object with `code` and `message`. Known failure codes include `item_unavailable`, `price_changed`, `insufficient_funds`, and `cancelled`. Webhook behavior: - The API sends `order.updated` events to the `webhookUrl` submitted with the order. - Webhook requests use a 10 second timeout. - Failed callbacks retry up to 10 attempts with exponential backoff. - `sent` orders must be accepted within 10 minutes (trade window). - Webhook handlers must verify the signature before trusting the payload. - Webhook handlers must use the raw request body bytes/string for verification, before JSON parsing changes whitespace or formatting. Webhook headers: ```http x-event-type: order.updated x-delivery-id: 01971d02-... x-timestamp: 2026-04-25T12:36:12.000Z x-signature: ``` Verify webhooks by computing an HMAC-SHA256 hex digest over: ```text . ``` Use the same client secret you send in `x-client-secret` as the HMAC key. Compare the provided `x-signature` with a timing-safe comparison. Node.js verification example: ```js import crypto from "node:crypto"; export function verifyOrderWebhook({ clientSecret, timestamp, rawBody, signature, }) { const expected = crypto .createHmac("sha256", clientSecret) .update(`${timestamp}.${rawBody}`) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex"), ); } ``` Webhook payload shape: ```json { "externalId": "order_100042", "status": "completed", "steamOfferId": "7193849201", "amount": "1840", "currency": "USD", "error": null, "timestamps": { "...": "ISO datetimes" } } ``` HTTP errors to handle: - `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. Implementation requirements: - Keep client credentials server-side. - Use environment variables for client credentials (for example `CLIENT_ID`, `CLIENT_SECRET`), or your deployment-specific names. - Add request timeouts and structured error handling. - Do not retry `POST /v1/orders` with a different `externalId` for the same customer order. - Refresh the catalog before checkout because inventory availability can change. - Persist order state locally and update it from verified webhooks. - Make webhook processing idempotent by using `x-delivery-id` and/or the latest stored order status. - Do not invent endpoints or fields. Use only the contract in this prompt unless the API documentation says otherwise.