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
;
@skinshark/sdk/types subpath — that’s the canonical source.