Skip to main content
Core API is the single-account integration model. Your merchant key is the only identity that exists — every trade, deposit, and withdrawal posts to that one account. No sub-users, no On-Behalf-Of, no inter-account transfers. This is the right starting point if you operate one storefront, one treasury, one consolidated ledger.

Mental model

When this fits

  • One product, one storefront, one treasury.
  • You don’t need per-customer balance isolation in SkinShark.
  • All reconciliation can happen at one merchant level.
  • Operations is a single team, not multi-tenant.
If any of these don’t apply, jump to Full Platform.

Endpoints you’ll use

All sub-user-context routes work without an On-Behalf-Of header — they run as the merchant. So you only ever need:

Skip these (for now)

These endpoints are real, just not relevant in Core API mode:
  • /merchant/users/* — sub-user CRUD and funding
  • /merchant/users/{id}/wallet — sub-user wallet reads
  • On-Behalf-Of header — never needed
You can adopt them later without breaking anything; they’re additive.

Daily operation

A typical day in Core API:
1

User browses

Your frontend calls GET /market/search and GET /market/items/{itemId}/listings to render inventory.
2

User buys

POST /market/buy debits your merchant wallet by totalPrice and creates a trade.
3

Server confirms

A trade.completed webhook fires (or your WebSocket subscriber sees trade.completed). Persist the trade against your own order table by externalId.
4

Reconcile

Nightly job calls GET /merchant/ledger?type=spot and GET /merchant/stats against yesterday’s window for accounting.
5

Top up

When the merchant balance dips, deposit via Gate Pay / on-ramp / crypto. The deposit credits the merchant wallet directly.

Cancelling an order

tradeRef is the trade id or the externalId you sent. itemRef is TradeItem.id or the per-item externalId you sent — set one on every item you might want to cancel later:
For quick buys this is the only way to tell your units apart. Fills arrive over time and in no fixed order, and a quick-buy item has no listing of its own to name. An item is cancellable only while initiated or pending, once the order reached the supplier, and no earlier than 30 minutes after creation — before that you get TRADE_CANCEL_TOO_SOON with cancellableAt. Anything already delivered or in flight to Steam is TRADE_NOT_CANCELLABLE. Both are best-effort. The trade-level call returns 200 even when some items are refused, with a per-item status and reason, so check the body rather than the status code. A successful cancel refunds in full with no penalty, and the money arrives asynchronously via trade.refunded — the response only confirms the supplier accepted.

Code template

What you give up

  • No tenant isolation. All trades show up under one merchant. If a customer disputes, they’re not separable in SkinShark’s view.
  • One ledger. Per-customer accounting is your job, not SkinShark’s.
  • Migration cost later. If your business needs per-customer wallets, you’ll move to Full Platform — usually a one-time refactor where you swap one api() call signature to take a sub-user ID.

When to graduate

Move to Full Platform when you need:
  • Per-customer balance isolation (chargeback containment, audit clarity).
  • Multi-tenant reporting in the merchant dashboard.
  • Per-customer fee overrides.
  • Self-service crypto deposits per customer.
The migration is mostly mechanical: provision a sub-user per customer, add On-Behalf-Of, route customer-attributable calls there. The catalog and trade primitives don’t change.