API reference
Every Copy Trading endpoint — user and admin — with the permission each admin route gates on, the rate limits, the WebSocket channel contract, the eight tables and the enum values you will see in them.
Two API surfaces. Everything under /api/copy-trading is scoped to the calling
user and carries no permission — it is gated by KYC features and rate limits
instead. Everything under /api/admin/copy-trading carries an explicit permission.
Conventions that will catch you out
The platform pins the HTTP status at 200 and puts the real outcome in the body. Read the body, always. A follow request that was refused for a market conflict returns a body saying so; a client branching on the status code will render it as a successful subscription.
Amounts on these tables are DECIMAL and FLOAT columns and arrive as strings
in raw form. Coerce before arithmetic — "1" + 1 is "11".
Every money figure carries its own currency. There is no implied USDT. A response that aggregates across markets says which asset it is in, or says that it spans more than one.
User endpoints
Discovery
The caller's own view
Leading
eligibility accepts a tradingType query parameter (SPOT, BINARY or BOTH,
default SPOT) and evaluates the track record for that class, because the trade
count and win rate are drawn from different histories.
apply requires displayName, tradingStyle, riskLevel and at least one
markets entry. tradingType defaults to SPOT; profitSharePercent defaults to
10 and is bounded 0–50.
Following
Allocations
follow takes leaderId and an allocations array. Each entry needs a symbol
and takes marketType (SPOT or BINARY, default SPOT), baseAmount and
quoteAmount. A BINARY entry has its baseAmount forced to zero and requires a
quoteAmount above zero.
The fund endpoints take amount and currency, where currency is the literal
string BASE or QUOTE — not a ticker. A BASE operation on a binary allocation
is refused, because a binary allocation holds only a quote stake budget.
KYC gates on user routes
| Feature | Guards |
|---|---|
copy_traders |
POST /api/copy-trading/follower/follow |
become_trader |
Leader eligibility, once copyTradingRequireKYC is on and feature enforcement is enabled |
Neither binds until Enforce KYC Feature Access is switched on in core settings.
Admin endpoints
Leaders
Subscriptions, trades and money
Every admin route is additionally rate-limited to 50 actions per hour. The
settings screen is not in this surface — it writes through
/api/admin/system/settings and is governed by the core settings permission.
WebSocket
Connect to /api/copy-trading. Authentication is required.
{ "action": "SUBSCRIBE", "payload": { "channel": "my_trades" } }{ "action": "SUBSCRIBE", "payload": { "channel": "leader_updates", "leaderId": "…" } }UNSUBSCRIBE takes the identical payload. Subscribing returns an initial snapshot
in the response, so a client does not need a separate fetch on connect.
| Channel | Snapshot on subscribe | Streams |
|---|---|---|
my_trades |
Up to 50 live copies across the caller's subscriptions | my_trade, notification, subscription_update |
my_subscriptions |
The caller's subscriptions with each leader's ROI and win rate | — |
leader_updates |
That leader's profile | leader_trade, leader_stats |
all_leaders |
The top 50 active public leaders by ROI | leaderboard, leaderboard_update |
The client registry is per process. On a multi-process or worker-thread deployment, an event raised where the copy was written may not reach a socket held elsewhere. Build any integration to reconcile against the REST endpoints on a timer, exactly as the product's own screens do.
Tables
If you read the database directly:
| Table | Holds |
|---|---|
copy_trading_leaders |
Leader profiles. Soft-deleted, so filter deletedAt |
copy_trading_leader_markets |
(leaderId, symbol, marketType) with minBase, minQuote and isActive |
copy_trading_followers |
Subscriptions. Soft-deleted |
copy_trading_follower_allocations |
(followerId, symbol, marketType) with base/quote amounts and their committed portions |
copy_trading_trades |
Leader trades and follower copies in one table — isLeaderTrade separates them |
copy_trading_transactions |
The money trail, with balanceBefore / balanceAfter and a currency on every row |
copy_trading_leader_stats |
One row per leader per UTC day, denominated in USDT |
copy_trading_audit_logs |
The trail. entityType and entityId are the only join; there is no followerId column |
Enum values
| Column | Values |
|---|---|
copy_trading_leaders.status |
PENDING, ACTIVE, SUSPENDED, REJECTED, INACTIVE |
copy_trading_leaders.tradingType |
SPOT, BINARY, BOTH |
copy_trading_leaders.tradingStyle |
SCALPING, DAY_TRADING, SWING, POSITION |
copy_trading_leaders.riskLevel |
LOW, MEDIUM, HIGH |
copy_trading_followers.status |
ACTIVE, PAUSED, STOPPED |
copy_trading_followers.copyMode |
PROPORTIONAL, FIXED_AMOUNT, FIXED_RATIO |
copy_trading_trades.status |
PENDING, PENDING_REPLICATION, REPLICATED, REPLICATION_FAILED, OPEN, PARTIALLY_FILLED, CLOSING, CLOSED, CANCELLED, FAILED |
copy_trading_trades.marketType |
SPOT, BINARY |
copy_trading_transactions.type |
ALLOCATION, DEALLOCATION, PROFIT_SHARE, TRADE_PROFIT, TRADE_LOSS, FEE, REFUND |
copy_trading_transactions.status |
PENDING, COMPLETED, FAILED |
Three columns on copy_trading_trades are the linkage keys and are worth knowing:
leaderOrderId is the leader's venue order id and is what deduplication and
teardown key on; followerOrderId is the follower's own order id and is what
lets a cancel or a fill find their order; closeOrderId is the exit order placed
by a stop-loss or take-profit.