LU14ยทMerchant

API Reference

A small REST API to buy CS2 skins at wholesale and have them delivered straight to a Steam trade URL. Deposit once, then order programmatically - items ship from our bots automatically, 24/7.

Introduction

All endpoints live under the base URL below and return JSON. You provision API keys in your merchant portal (or we issue them during onboarding).

Base URL
https://api.your-domain.com/api/v1

Money is always sent two ways: an integer *_milli field (1 USD = 1000 milli-USD, the exact source of truth) and a convenience *_usd float. Compute with the integer; display the float.

Authentication

Send your key in the x-api-key header on every request. Keys are tied to your account; a suspended account is rejected with 403.

curl -H "x-api-key: lum_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  "https://api.your-domain.com/api/v1/balance"
Treat keys like passwords. The full key is shown once when created - store it securely. You can revoke and rotate keys anytime in the portal.

Responses & money

Every response uses the same envelope:

{
  "success": true,
  "msg": null,
  "data": { ... }
}

On failure, success is false, msg carries a human-readable reason, and data is null. List endpoints accept limit (1-200, default 50) and offset for paging.

Errors

Standard HTTP status codes, with the reason in msg.

StatusMeaning
200Success.
400Bad request - missing/invalid field (e.g. malformed trade_url).
401Missing or invalid API key.
403Account suspended, or insufficient balance for an order.
404Resource not found.
409Conflict - no matching item available, or a reused custom_id clash.
{ "success": false, "msg": "insufficient balance", "data": null }

Quickstart

Check a price, place an order, then poll until it settles.

# 1. cheapest price + how many are available for a name
curl -H "x-api-key: $KEY" \
  "https://api.your-domain.com/api/v1/items/price?market_hash_name=AK-47%20%7C%20Redline%20(Field-Tested)"

# 2. buy the cheapest match under $12.50, delivered to a trade URL
curl -X POST -H "x-api-key: $KEY" -H "content-type: application/json" \
  "https://api.your-domain.com/api/v1/orders" -d '{
    "custom_id": "order-0001",
    "market_hash_name": "AK-47 | Redline (Field-Tested)",
    "max_price": 12.50,
    "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc"
  }'

# 3. poll the order until status is completed or failed
curl -H "x-api-key: $KEY" "https://api.your-domain.com/api/v1/orders/ORDER_ID"

List items

GET/api/v1/items

Available items, cheapest first. Each row is a single concrete asset you can buy by id.

QueryTypeDescription
market_hash_namestringoptionalExact item name filter.
min_price / max_pricenumber (USD)optionalPrice bounds.
limit / offsetintegeroptionalPaging (limit 1-200, default 50).
Response
{
  "success": true,
  "msg": null,
  "data": {
    "items": [
      {
        "assetid": "44965530837",
        "market_hash_name": "AK-47 | Redline (Field-Tested)",
        "float_value": 0.23,
        "phase": null,
        "price_milli": 11820,
        "price_usd": 11.82
      }
    ],
    "limit": 50,
    "offset": 0
  }
}

Get price

GET/api/v1/items/price

The cheapest available price and how many units are in stock for one name - handy before placing a by-name order.

Request
curl -H "x-api-key: $KEY" \
  "https://api.your-domain.com/api/v1/items/price?market_hash_name=AK-47%20%7C%20Redline%20(Field-Tested)"
Response
{
  "success": true,
  "msg": null,
  "data": {
    "market_hash_name": "AK-47 | Redline (Field-Tested)",
    "available": 14,
    "price_milli": 11820,
    "price_usd": 11.82
  }
}

Order lifecycle

An order moves through these states:

  • pending - accepted; price reserved and debited from your balance.
  • sending - a trade offer is on its way to your trade URL.
  • completed - the item was delivered.
  • failed - delivery did not happen; the full amount is automatically refunded to your balance.
Funds are debited the moment an order is accepted and refunded automatically if it ends up failed - every movement appears in transactions.

Place an order

POST/api/v1/orders

Buy by name (cheapest match, optionally capped by max_price) or by id (a specific assetid from list items). Provide exactly one of the two.

FieldTypeDescription
custom_idstringrequiredYour idempotency key. Reusing it returns the original order instead of creating a new one.
trade_urlstringrequiredThe Steam trade URL items are delivered to.
market_hash_namestringone ofBuy the cheapest available item with this name.
assetidstringone ofBuy this specific asset.
max_pricenumber (USD)optionalFor by-name: skip if the cheapest match exceeds this.
Request
curl -X POST -H "x-api-key: $KEY" -H "content-type: application/json" \
  "https://api.your-domain.com/api/v1/orders" -d '{
    "custom_id": "order-0001",
    "market_hash_name": "AK-47 | Redline (Field-Tested)",
    "max_price": 12.50,
    "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc"
  }'
Response
{
  "success": true,
  "msg": null,
  "data": {
    "id": "64bcd6a8-7fec-4139-8f7c-6018978a9a00",
    "custom_id": "order-0001",
    "status": "pending",
    "mode": "by_name",
    "requested_name": "AK-47 | Redline (Field-Tested)",
    "requested_assetid": null,
    "matched_assetid": "44965530837",
    "price_milli": 11820,
    "price_usd": 11.82,
    "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
    "failure_reason": null,
    "tradeofferid": null,
    "created_at": "2026-06-08T08:03:38Z",
    "completed_at": null
  }
}
Idempotency: always send a unique custom_id per intended purchase and retry with the same one on network errors. You will never be double-charged for the same custom_id.

Get an order

GET/api/v1/orders/{id}

Fetch one order by its id. Poll this to follow delivery, or use webhooks (ask us to enable them). Returns the same object shape as place an order; on success tradeofferid is filled in and status becomes completed.

curl -H "x-api-key: $KEY" "https://api.your-domain.com/api/v1/orders/64bcd6a8-7fec-4139-8f7c-6018978a9a00"

List orders

GET/api/v1/orders

Your orders, newest first. Filter by status and page with limit / offset.

curl -H "x-api-key: $KEY" "https://api.your-domain.com/api/v1/orders?status=completed&limit=20"

Balance

GET/api/v1/balance
{
  "success": true,
  "msg": null,
  "data": { "balance_milli": 250500, "balance_usd": 250.5 }
}

Transactions

GET/api/v1/transactions

Your append-only ledger - deposits, order debits, refunds, and adjustments, newest first, each with the resulting balance.

{
  "success": true,
  "msg": null,
  "data": {
    "transactions": [
      {
        "id": 42,
        "kind": "debit",
        "amount_milli": -11820,
        "amount_usd": -11.82,
        "balance_after_milli": 238680,
        "balance_after_usd": 238.68,
        "order_id": "64bcd6a8-7fec-4139-8f7c-6018978a9a00",
        "note": "order",
        "created_at": "2026-06-08T08:03:38Z"
      }
    ],
    "limit": 50,
    "offset": 0
  }
}

Need higher limits or help integrating? Talk to us on Telegram.

Webhooks

Configure a webhook endpoint in the portal settings. We will POST a signed JSON payload to that URL every time an order changes state.

Events: order.pending, order.sending, order.completed, order.failed.

Failed deliveries are retried with exponential backoff (30s, 2m, 10m, 1h, 4h) up to five attempts.

Verifying signatures

Every webhook request includes an X-LU14-Signature header. The value is sha256= followed by the HMAC-SHA256 hex digest of the raw request body, keyed with your signing secret.

Always verify before acting on the payload. Use a constant-time comparison to prevent timing attacks.

Python
import hmac, hashlib

def verify(secret: str, body: bytes, header: str) -> bool:
    digest = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest("sha256=" + digest, header)
Node.js
const crypto = require("crypto");

function verify(secret, body, header) {
  const digest = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(header));
}
The secret is shown once when you save or regenerate it in portal settings. Rotate immediately if it leaks.

Payload

The body is JSON. All fields are present on every event; fields that do not apply for the current state will be null.

FieldTypeDescription
eventstringOne of the four event names above.
order_idstring (UUID)Our order identifier.
custom_idstringThe custom_id you supplied when placing.
statusstringCurrent order status.
price_milliintegerAmount charged in milli-USD.
price_usdfloatSame, as a USD float.
failure_reasonstring or nullPopulated on order.failed.
tradeofferidstring or nullSteam trade offer ID, once a send is confirmed.
created_atISO 8601When the order was placed.
completed_atISO 8601 or nullWhen the order reached a terminal state.
Example - order.completed
{
  "event": "order.completed",
  "order_id": "64bcd6a8-7fec-4139-8f7c-6018978a9a00",
  "custom_id": "order-0001",
  "status": "completed",
  "price_milli": 11820,
  "price_usd": 11.82,
  "failure_reason": null,
  "tradeofferid": "5249817634",
  "created_at": "2026-06-08T08:03:38Z",
  "completed_at": "2026-06-08T08:05:12Z"
}

Need help integrating? Talk to us on Telegram.