Skip to main content
These types mirror the OpenAPI data shapes — what’s inside body.data on a successful envelope. Generate them with openapi-typescript (TypeScript types guide) or import from @skinshark/sdk/types.

Enums

type Currency = "USD" | "EUR";

type Role = "user" | "merchant" | "admin";

type UserStatus = "active" | "suspended" | "deleted";

type WalletType = "spot" | "earnings";

type WalletStatus = "active" | "suspended" | "closed";

type TradeStatus =
  | "initiated"
  | "pending"
  | "active"
  | "hold"
  | "completed"
  | "failed"
  | "reverted";

type TradeType = "buy" | "sell";

type RevertedBy = "supplier" | "user";

type DeliveryMode = "standard" | "instant";

type DepositStatus =
  | "initiated"
  | "pending"
  | "completed"
  | "partial"
  | "expired"
  | "cancelled"
  | "refunded"
  | "failed";

type DepositMethod = "gatepay" | "onramp" | "crypto";

type DepositCurrency = "USDT" | "USDC" | "DAI" | "BTC" | "ETH" | "SOL";

type OnrampPayCurrency = "USD" | "EUR" | "GBP";

type CryptoEvmChain = "ethereum" | "base" | "arbitrum" | "optimism" | "bsc";

type CryptoNativeToken = "USDT" | "USDC" | "NATIVE";

type TwoFactorMethod = "email" | "totp";

// CS2 community labels keep their natural casing.
type ItemCategory = "Normal" | "StatTrak" | "Souvenir";
type DopplerPhase =
  | "Phase 1"
  | "Phase 2"
  | "Phase 3"
  | "Phase 4"
  | "Ruby"
  | "Sapphire"
  | "Black Pearl"
  | "Emerald";

type SearchSort = "relevance" | "nameAsc" | "nameDesc" | "priceAsc" | "priceDesc";

Envelope

interface SuccessEnvelope<T> {
  requestId: string;
  success: true;
  data: T;
}

interface ErrorEnvelope {
  requestId: string;
  success: false;
  error: {
    code: number;
    key: string;
    message: string;
    meta?: Record<string, unknown>;
  };
}

type ApiEnvelope<T> = SuccessEnvelope<T> | ErrorEnvelope;

Merchant context

interface MerchantProfile {
  id: string;
  email: string;
  emailVerified: boolean;
  country: string | null;
  roles: Role[];
  twoFactorEnabled: boolean;
  twoFactorMethod: TwoFactorMethod | null;
  feeBps: number;             // house fee on merchant's own buys
  merchantFeeBps: number;     // merchant's cut on sub-user trades
  childDefaultFeeBps: number | null;
  wallets: {
    spot: { currency: string; balance: number } | null;
    earnings: { currency: string; balance: number } | null;
  };
  createdAt: string;
}

interface FeesResponse {
  merchantFeeBps: number;
  childDefaultFeeBps: number | null;
  globalDefaultFeeBps: number;
  effectiveChildFeeBps: number;
}

interface MerchantStats {
  totals: {
    tradeCount: number;
    gmv: number;
    gmvCents: number;            // exact integer cents
    feesEarnedCents: number;
    feesEarned: number;
    spotBalance: number;
    earningsBalance: number;
    currency: Currency | null;
  };
  bySubUser: Array<{
    subUserId: string;
    email: string | null;
    externalId: string | null;
    tradeCount: number;
    gmvCents: number;
    gmv: number;
    balance: number;
    feesContributedCents: number;
  }>;
  byStatus: Partial<Record<TradeStatus, number>>;
}

interface MerchantWallet {
  walletId: string;
  type: WalletType;
  currency: Currency;
  balance: number;
}

Sub-users

interface SubUserListItem {
  id: string;
  email: string | null;
  steamId: string | null;
  externalId: string | null;
  status: UserStatus;
  feeBps: number | null;
  currency: Currency | null;
  balance: number;
  createdAt: string;
}

interface SubUserListResponse {
  items: SubUserListItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

interface CreateSubUserBody {
  email?: string;
  steamId?: string;            // SteamID64
  externalId?: string;         // also acts as the idempotency key for create
  currency?: Currency;
}

interface CreateSubUserResponse {
  id: string;
  email: string | null;
  steamId: string | null;
  externalId: string | null;
  status: UserStatus;
  feeBps: number | null;
  createdAt: string;
  currency: Currency;
  /** True when the response replays a prior create with the same externalId. */
  idempotent: boolean;
}

interface SubUserResponse {
  id: string;
  email: string | null;
  steamId: string | null;
  externalId: string | null;
  status: UserStatus;
  feeBps: number | null;
  createdAt: string;
  wallet: {                    // single object — sub-users only have one wallet
    type: WalletType;
    status: WalletStatus;
    currency: Currency | null;
    balance: number;
  } | null;
}

interface FundBody {
  amount: string;              // decimal, e.g. "25.00"
}

interface FundResponse {
  transactionId: string;
  idempotent: boolean;
}

Wallet & ledger

interface WalletBalance {
  currency: Currency;
  balance: number;
}

interface TransactionItem {
  id: string;
  type: string;                // "deposit_credit" | "trade_payment" | ...
  description: string;
  status: "posted" | "reversed";
  amount: number;              // signed: + = credit, − = debit
  occurredAt: string;
  externalRef: string | null;
}

interface TransactionHistory {
  items: TransactionItem[];    // /user/wallet/ledger, /merchant/ledger
  nextCursor: string | null;
}

Trades

interface TradeItem {
  id: string;
  name: string | null;
  marketHashName: string | null;
  type: string | null;
  iconUrl: string | null;
  price: number;
  exterior?: string;
  rarity?: string;
  color?: string;
  phase?: string;
  wear?: string;
  paintSeed?: number;
  stickers?: Array<{
    name: string;
    slot: number;
    wear: number;
    iconUrl: string;
  }>;
  charm?: { name: string; pattern?: string; iconUrl: string };
  delivery?: string;
  status: TradeStatus;
  tradable: boolean;
  error?: string;
}

interface Trade {
  id: string;
  type: TradeType;
  userId: string;
  steamId: string;
  tradeUrl: string;
  offerId?: string;
  externalId?: string;
  status: TradeStatus;
  game: string;                // "730"
  items: TradeItem[];
  summary: { total: number; completed: number; failed: number };
  totalPrice: number;
  currency: Currency;
  holdEndDate?: string;
  settledAt?: string;
  revertedBy?: RevertedBy;
  error?: string;
  createdAt: string;
  updatedAt: string;
}

// /merchant/trades, /merchant/users/{id}/trades, /market/transactions
interface TradeListResponse {
  items: Trade[];
  nextCursor: string | null;
}

interface BuyItem {
  // Provide exactly one of listingId or listingRawId.
  listingId?: string;          // from MarketListing.id
  listingRawId?: string;       // raw listing id
  maxPrice: string;            // decimal string
}

interface BuyBody {
  items: BuyItem[];            // 1–10
  tradeUrl?: string;
  externalId?: string;         // your correlation id; replays return the same Trade
}

interface BuyResponse {
  id: string;
  status: TradeStatus;         // "initiated" right after buy
  itemCount: number;
  totalPrice: number;
  createdAt: string;
}

interface QuickBuyBody {
  itemId: string;
  maxPrice: string;
  amount: number;              // 1–200
  delivery: DeliveryMode;
  phase?: DopplerPhase;        // EcoSteam-only; cannot combine with delivery: "instant"
  tradeUrl?: string;
  externalId?: string;
}

interface QuickBuyResponse {
  id: string;
  status: TradeStatus;
  requestedCount: number;
  itemCount: number;
  totalPrice: number;
  createdAt: string;
}

interface CancelItemResponse {
  ok: true;
  itemId: string;
  status: "cancelled" | "failed";
}

Catalog & listings

interface SuggestionItem {
  id: string;
  marketHashName: string;
  iconUrl: string;
  rarity?: string;
  rarityColor?: string;
  itemType?: string;
}

interface SearchResultItem {
  id: string;
  name: string;
  marketHashName: string;
  itemType?: string;
  iconUrl?: string;
  weapon?: string;
  wear?: string;
  rarity?: string;
  rarityColor?: string;
  category?: ItemCategory;
  collection?: string;
  floatRange?: [number, number];
  // Both fee-applied. standard = overall lowest (manual); instant = auto-deliver
  // floor (C5 today), null when none. Omitted when no market price.
  price?: { standard: number; instant: number | null };
  listingsCount?: number;
  steamPrice?: number;
}

interface SearchResponse {
  items: SearchResultItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

interface MarketListing {
  id: string;
  marketHashName: string;
  name: string;
  type: string;
  iconUrl: string;
  price: number;
  referencePrice?: number;
  exterior?: string;
  rarity?: string;
  collection?: string;
  color?: string;
  wear?: number | null;
  paintSeed?: number | null;
  doppler?: { status: number; name: string; paintIndex?: number };
  fade?: { percentage: number };
  hardened?: { status: number; name: string };
  stickers?: Array<{
    name: string;
    marketHashName?: string;
    slot: number;
    wear?: number;
    iconUrl: string;
  }>;
  charm?: {
    name: string;
    marketHashName?: string;
    pattern?: string;
    iconUrl: string;
  };
  inspectUrl?: string;
  delivery?: string;
}

// /market/items/{itemId}/listings
interface ListingsResponse {
  items: MarketListing[];
  total: number;
}

// /market/listings/{listingId} — bare resource, no wrapper
type ListingDetail = MarketListing;

Deposits

// Gate Pay
interface DepositChainsResponse {
  currencies: Array<{
    currency: DepositCurrency;
    chains: string[];
    isStable: boolean;
    minDepositUsd: number;
  }>;
}

interface DepositQuoteBody {
  currency: DepositCurrency;
  amount: string;              // decimal USD
}

interface DepositQuoteResponse {
  token: DepositCurrency;
  currency: Currency;
  payAmountToken: number;
  receiveAmountUsd: number;
  receiveAmount: number;
  fee: number;
  exchangeRate: number;
  quoteToken: string;
  expiresIn: string;           // "5m"
}

interface CreateDepositBody {
  quoteToken: string;
  chain: string;
}

interface CreateDepositResponse {
  fundingId: string;
  status: DepositStatus;
  currency: Currency;
  payAmountToken: number;
  receiveAmountUsd: number;
  receiveAmount: number;
  exchangeRate: number;
  fee: number;
  expireTime: number | null;
  whitelabelUrl: string | null;
  onChain: { network: string; token: DepositCurrency; address: string };
}

// On-ramp (card)
interface OnrampQuoteBody {
  payAmount?: number;
  receiveAmount?: number;
  payCurrency: OnrampPayCurrency;
}

interface OnrampSessionResponse {
  fundingId: string;
  status: "initiated" | "completed" | "failed";
  currency: Currency;
  payAmount: number;
  receiveAmount: number;
  receiveAmountUsd: number;
  payCurrency: OnrampPayCurrency;
  exchangeRate: number;
  whitelabelUrl: string;
}

// Self-hosted EVM crypto
interface CryptoAddressResponse {
  address: string;             // 0x...
  chains: CryptoEvmChain[];
  tokens: CryptoNativeToken[];
}

interface CryptoQuoteBody {
  token: "USDT" | "USDC";
  amount: number;              // ≤ 10,000,000
}

interface CryptoQuoteResponse {
  token: string;
  amount: number;
  walletCurrency: Currency;
  exchangeRate: number;
  feeBps: number;
  chains: Record<CryptoEvmChain, {
    feeCents: number;
    receiveAmountUsd: number;
    receiveAmount: number;
    gasGwei: number;
  }>;
}

interface CancelDepositResponse {
  depositId: string;
  status: DepositStatus;
}

interface ResumeDepositResponse {
  fundingId: string;
  status: DepositStatus;
  method: DepositMethod;
  currency: Currency;
  amount: number;
  whitelabelUrl: string | null;
  onChain: { network: string; token: string; address: string } | null;
}

Partner crypto payout custody

type PayoutCryptoToken = "USDT" | "USDC";

type PayoutWithdrawalStatus =
  | "pending_callback"
  | "queued"
  | "broadcast"
  | "confirmed"
  | "failed"
  | "refunded";

// GET /user/wallet/payout/crypto/address — distinct from /deposit/crypto/address.
interface PayoutCryptoAddressResponse {
  address: string;                  // 0x...
  chains: CryptoEvmChain[];
  tokens: PayoutCryptoToken[];
}

// One row per (chain, tokenAddress). Each row is an independent withdrawable bucket.
// No cross-chain liquidity — funds on chain X can only be withdrawn on chain X.
interface PayoutCryptoBalance {
  chain: string;                    // ethereum | base | arbitrum | optimism | bsc
  token: string;                    // USDT | USDC
  tokenAddress: string;             // ERC-20 contract on this chain (lowercased)
  balanceCents: string;             // USD cents, bigint as decimal string
}

interface PayoutBalancesResponse {
  balances: PayoutCryptoBalance[];
}

interface PayoutQuoteBody {
  chain: CryptoEvmChain;
  token: PayoutCryptoToken;
  amountCents: string;              // bigint as decimal string
}

// All fees pre-multiplied by the configured payout fee multiplier (default 1.5).
// L2 chains (Base, Arbitrum, Optimism) return all-"0" stats.
interface PayoutQuoteStats24h {
  minFeeUsdCents: string;
  p25FeeUsdCents: string;
  avgFeeUsdCents: string;
  p75FeeUsdCents: string;
  maxFeeUsdCents: string;
}

interface PayoutQuoteResponse {
  chain: string;
  token: string;
  amountCents: string;
  liveFeeUsdCents: string;          // live network gas; "0" on L2 chains
  liveTotalDebitCents: string;      // amountCents + liveFeeUsdCents
  stats24h: PayoutQuoteStats24h;
  computedAt: string;               // ISO-8601
}

interface PayoutWithdrawBody {
  chain: CryptoEvmChain;
  token: PayoutCryptoToken;
  destination: string;              // 0x...
  amountCents: string;              // bigint USD cents
  externalId: string;               // your idempotency key, unique per merchant
  forSubUser?: string;              // sub-user UUID OR your externalId for them; label only
  maxFeeUsdCents?: string;          // optional cap; fee > cap ⇒ 1821 (no state change)
}

interface PayoutWithdrawResponse {
  id: string;
  status: PayoutWithdrawalStatus;   // "pending_callback" on success
  chain: string;
  token: string;
  destination: string;
  amountCents: string;
  feeCents: string;
  externalId: string;
  forUserId: string | null;
  forUserExternalId: string | null;
  createdAt: string;
}

interface PayoutWithdrawal {
  id: string;
  status: PayoutWithdrawalStatus;
  chain: string | null;
  token: string | null;
  tokenAddress: string | null;
  destination: string | null;
  amountCents: string;
  feeCents: string;
  txHash: string | null;
  failureReason: string | null;
  externalId: string | null;
  forUserId: string | null;
  forUserExternalId: string | null;
  callbackAttempts: number;
  createdAt: string;
  broadcastAt: string | null;
  confirmedAt: string | null;
}

interface PayoutWithdrawalListResponse {
  items: PayoutWithdrawal[];
  nextCursor: string | null;
}

Sub-user profile & trade URLs

interface UserProfile {
  id: string;
  email: string;
  emailVerified: boolean;
  country: string | null;
  roles: Role[];
  twoFactorEnabled: boolean;
  twoFactorMethod: TwoFactorMethod | null;
  steam: {
    steamId: string;
    personaName: string | null;
    avatarUrl: string | null;
    profileUrl: string | null;
    tradeUrl: string;
  } | null;
  discord: {
    discordId: string;
    username: string;
    avatar: string | null;
  } | null;
  wallet: { currency: Currency; balance: number } | null;
  createdAt: string;
}

interface TradeUrlResponse {
  id: string;
  url: string;
  steamId: string;
  personaName: string | null;
  avatarUrl: string | null;
  profileUrl: string | null;
  isPrimary: boolean;
  createdAt: string;
}

WebSocket events

interface WsConnectedEvent {
  event: "connected";
  data: { userId: string };
  ts: number;
}

interface WsTradeUpdateEvent {
  event: `trade.${string}`;
  data: Trade;
  ts: number;
}

interface WsSystemNoticeEvent {
  event: "system.notice";
  data: { severity: "info" | "warning" | "critical"; message: string };
  ts: number;
}

type WsEvent =
  | WsConnectedEvent
  | WsTradeUpdateEvent
  | WsSystemNoticeEvent
  // … plus deposit variants documented in /guides/websocket
  ;
For exhaustive type coverage, generate types from the OpenAPI spec or use the @skinshark/sdk/types subpath — that’s the canonical source.