API Reference
Copy Trading

Copy Trading API Routes

Copy trading routes exist in two layers:

LayerWhat it doesLocation
Insiders backend /api/copytradeUser-facing subscription management, history, top traderspolyinsiders-backend-v1
Trading Tools v2 control-api :9110Engine-level subscription CRUD, live PnL, fills, perf actions/opt/tradingtools-v2/apps/control-api

The backend /api/copytrade routes proxy to the trading tools v2 control-api for the heavy engine work. The control-api is internal (loopback :9110) and requires a shared Bearer token.


Insiders Backend · /api/copytrade

Codebase: polyinsiders-backend-v1/src/routes/copytrade.routes.ts

Public discovery

MethodPathAuthDescription
GET/top-tradersTop traders ranked by performance
GET/trader/:addressTrader profile + stats

User subscriptions

MethodPathAuthDescription
GET/history🔒Copy trade execution history
POST/subscriptions🔒Create a copy subscription
GET/subscriptions🔒List active subscriptions
GET/subscriptions/:id🔒Get subscription + live PnL
PATCH/subscriptions/:id🔒Update subscription settings
DELETE/subscriptions/:id🔒Cancel subscription

Trading Tools v2 · control-api :9110

Codebase: /opt/tradingtools-v2/apps/control-api/src/index.ts
Spec: /opt/tradingtools-v2/docs/control_api_wire_spec_v1.md

⚠️

Not browser-callable. Terminal devs call via the backend proxy. The proxy resolves user → FollowerIdentity and attaches Authorization: Bearer <CONTROL_API_TOKEN>.

Call path

Frontend → Insiders API backend → control-api :9110

Identity shape (required in every create body)

follower: {
  followerUserId: string      // backend users.id
  followerWalletId: string    // Privy custodial wallet id (EOA = 400)
  followerProxy: string       // 0x… deposit/proxy address
}

Subscription endpoints

MethodPathDescription
POST/subscriptionsCreate copy subscription → 201
GET/subscriptions?followerProxy=0x…List subs + live stats
GET/subscriptions/:idSingle sub + live PnL
GET/subscriptions/:id/fillsRaw copy fills (paged: ?limit=&offset=)
PATCH/subscriptions/:idModify sizing / filters / exitPolicy / performanceTriggers / autoSell
POST/subscriptions/:id/pauseStop copying new trades (positions remain)
POST/subscriptions/:id/resumeRe-arm subscription
DELETE/subscriptions/:idStop + remove from hot index (does NOT flatten positions)

TP/SL trigger endpoints (GUARD)

MethodPathDescription
POST/triggersCreate TP/SL on a copy position → 202
PATCH/triggers/:idModify trigger
DELETE/triggers/:idCancel trigger
GET/triggers?wallet=0x…List triggers for wallet
GET/triggers/:idGet single trigger

Internal endpoints (executor / perfmon only)

MethodPathDescription
POST/subscriptions/fundingFlip out-of-funds gate for all of a follower's subs
POST/subscriptions/:id/perf-actionSession PnL action: pause / set_multiplier / resume

Health

MethodPathAuthDescription
GET/healthnoneLiveness probe
GET/readynoneReadiness probe

POST /subscriptions — Full request shape

{
  follower: { followerUserId, followerWalletId, followerProxy },
  leaderProxy: string,                    // leader 0x… address
  sizing: {
    strategy: "fixed" | "proportional" | "capped",
    params:
      | { usd: number }                   // fixed
      | { pct: number }                   // proportional (0.5 = 50%)
      | { pct: number, minUsd: number, maxUsd: number }  // capped
  },
  autoSell: boolean,                      // mirror leader's sells
  filters?: {
    tradeSize?: { minUsd?: number, maxUsd?: number },   // A1
    priceBand?: { min: number, max: number },            // A2 (0–1)
    side?: "buy" | "sell",                              // A3
    category?: { allow?: string[], exclude?: string[] }, // A4
    volumeFloor?: { minUsd: number }                    // A5
  },
  exitPolicy?: {
    takeProfitPct?: number,               // 25 = exit at +25%
    stopLossPct?: number,
    trailingPct?: number
  },
  performanceTriggers?: {
    lossStopUsd?: number,
    profitScale?: { thresholdUsd?: number, thresholdPct?: number, multiplier: number }
  },
  enrichMissPolicy?: "copy" | "skip"      // default "copy"
}

Known cosmetic deviations (wire vs types)

  • capped sizing reads back as {pct, min, max} not {pct, minUsd, maxUsd} as sent
  • Mutation responses are bare {"success": true} — re-GET to read new state
  • SubscriptionView includes isFunded: boolean (not in published type)
  • exitPolicy may be {} when unset — treat missing keys as "not set"
  • DELETE is soft and idempotent — returns {"success": true} even for unknown IDs