The two admin edit forms, and what they bypass
The plain edit PUTs behind leaders and subscriptions — which fields they really write, which are silently discarded, what validation they skip, and why a status change here is not a teardown.
Beside the named actions on the admin console — approve, reject, suspend, activate, recalculate, delete for leaders; pause, resume, force stop for subscriptions — both entities also carry an ordinary edit endpoint that writes columns directly. Those two PUTs run none of the checks their named counterparts run, and none of the checks the customer-facing routes run on the very same fields.
They are worth knowing about for two opposite reasons. They are the only way to correct a mis-set risk parameter without asking the customer to do it. And they are the only way an administrator can put a leader or a subscription into a state the product's own rules forbid.
Where the edit forms actually are
Neither /admin/copy-trading/leader nor /admin/copy-trading/follower exposes
them. Both of those tables are mounted read-only — canEdit, canCreate and
canDelete are all off — so the row menu offers View plus the status actions and
no Edit item at all. The edit form definitions exist in those pages' column files,
but nothing renders them.
The one screen that does open them is Admin → CRM → Users → the user → the
Copy Trading tab (/admin/crm/user/{id}). The leader table embedded there is
mounted with edit and delete enabled. The subscription table below it has edit
enabled and delete switched off — which matches the API, where a leader has a
DELETE route and a subscription has none.
The Edit row action is not a dialog. It replaces the table with an inline edit form built from the table's columns, in place, inside the same CRM card — toolbar, rows and pagination all disappear until you save or cancel and it returns you to the list.
The CRM screen passes no form definition, so the form renders every column of the table and submits the whole row. What is actually written is decided at the other end, by a fixed allow-list in the endpoint. Fields outside that list are dropped without comment and the form reports the same outcome either way.
On the leader form only Display Name survives the trip. On the subscription form only Copy Mode and Status do.
Everything else in this page is reachable only by calling the endpoint directly.
Editing a subscription
Gated on edit.copy_trading and counted against the 50 actions per hour
limit. That limiter sits on the mutating admin copy-trading routes only — the two
PUTs, the named leader and subscription actions, the leader delete, the bulk
status change and the transaction reversal. Reading costs nothing: the lists, the
two detail GETs, analytics, audit and health carry no limiter and are not counted
against it. Every field is optional; only the fields present in the body are
considered.
| Field in the allow-list | What happens |
|---|---|
copyMode |
Written. PROPORTIONAL, FIXED_AMOUNT or FIXED_RATIO — the enum is enforced |
fixedAmount |
Written. No range check of any kind |
fixedRatio |
Written. No range check of any kind |
maxTradeAmount |
Discarded. No such column exists on the subscription |
riskMultiplier |
Discarded. No such column exists on the subscription |
stopLossPercent |
Written. Applies to positions that are already open |
takeProfitPercent |
Written. Applies to positions that are already open |
status |
Written as a bare value. ACTIVE, PAUSED or STOPPED |
maxTradeAmount and riskMultiplier are accepted by the request schema and then
silently dropped, because copy_trading_followers has neither column. The copy
engine reads riskMultiplier when it sizes a copy and falls back to 1 when it
is absent — which is every subscription, on every install. Treat it as a knob that
does not exist rather than one set to a default.
The two risk caps that matter cannot be edited here
maxDailyLoss and maxPositionSize are returned by this endpoint's response and
are shown on the subscription detail screen, but they are not in its
allow-list. There is no admin route that writes them.
That is unfortunate, because they are the two per-subscription settings with real teeth:
| Field | What enforces it |
|---|---|
maxDailyLoss |
A USDT amount of realised loss for the day. Reaching it pauses the subscription — checked as trades settle and again by a background job that sweeps every 60 seconds, so a breach is caught within a minute either way. A separate daily job, guarded to run once per UTC day, lifts the pause the next day — but only where a daily-loss pause is the latest thing that happened to the subscription and its leader is still ACTIVE |
maxPositionSize |
On spot, a hard ceiling on the copied quantity. On binary, a percentage of the allocation's total budget, clamped at 100 |
Only the follower can change them, from their own subscription settings. If one is set wrongly, the fix is a conversation with the customer, not a console action.
Changing status here is a column write, not a teardown
It writes the word STOPPED into the status column. It does not cancel live
copy orders, does not release the funds those orders have locked, does not
deactivate the per-market allocations, and does not move a single unit of
capital back to the follower's ECO or SPOT wallet. It also sends no notification,
so the customer is not told.
The money stays in the copy-trading wallet, committed to allocations behind a subscription that will never trade again. Nothing in the product sweeps that up.
The same warning applies in reverse. Writing ACTIVE over a STOPPED or PAUSED
subscription revives it with no checks at all — the dedicated resume action
refuses unless the subscription is currently PAUSED and its leader is
ACTIVE, and this endpoint asks neither question. A subscription revived behind a
suspended leader will not copy anything, but it does start counting again toward
both the leader's follower count and the follower's copyTradingMaxLeadersPerFollower
allowance.
Copy mode is not checked for consistency. The customer's own route refuses
FIXED_AMOUNT unless a positive fixed amount goes with it; this one does not. A
subscription left in FIXED_AMOUNT with no amount skips every copy with "Fixed
amount not configured" — it looks active and quietly does nothing. Switching to
FIXED_RATIO without a ratio is worse: the engine falls back to 0.1, so the
follower silently starts copying at a tenth of the leader's size.
Stop-loss and take-profit are live. The cron that enforces them reads the
current subscription row against every open spot position with a filled entry.
Tightening stopLossPercent on a subscription holding positions can close them at
market on the next pass.
This endpoint reports a failure even when it succeeds
The write and the audit entry are not in one transaction, and the audit entry
hands a raw object to a text column. Sequelize refuses that, so the request ends
in an error naming newValue — after the subscription row has already been
changed.
What you see is an error toast. What happened is:
- the columns were written, and stayed written;
- no audit row was recorded, for any edit made this way.
Re-submitting to "make it stick" writes the same values again and fails again. If you use this endpoint, verify the result by re-reading the subscription rather than by trusting the response, and record the change somewhere yourself.
Editing a leader
Same permission, same 50-per-hour limit. Eight fields, taken straight from the body onto the row:
| Field | Bound the route applies | Bound the leader's own route applies |
|---|---|---|
displayName |
None. The column's own 2–100 character rule still holds | Letters, digits, spaces, hyphens and underscores only |
bio |
None | Sanitised and truncated to 1000 characters |
tradingStyle |
None. A value outside SCALPING / DAY_TRADING / SWING / POSITION is the database's problem |
Rejected with a readable message |
riskLevel |
None. LOW / MEDIUM / HIGH, enforced by the column |
Rejected with a readable message |
profitSharePercent |
The column refuses anything outside 0–50 | 0–50 |
minFollowAmount |
Not below 0 | 0–1,000,000 |
maxFollowers |
Not below 1 | 1–10,000, and never below the current active follower count |
isPublic |
None | — |
The route itself validates nothing. What survives is whatever the column definitions happen to refuse, and the messages you get back are database messages, not product ones.
Settings records that copyTradingMaxProfitSharePercent is never
enforced and that the real ceiling is hard-coded at 50%. That ceiling lives on the
profitSharePercent column as well as in the customer route's validator, so an
admin edit above 50 is refused too — with the column's own message, and with the
rest of the edit refused alongside it.
The bounds that are not duplicated onto columns are the ones to watch:
minFollowAmount above a million, maxFollowers above ten thousand, a display
name containing anything at all, and an unsanitised bio.
Two more checks this endpoint skips:
- The customer's route refuses a profile edit unless the leader is
ACTIVE. This one will happily rewrite aPENDING,REJECTEDorSUSPENDEDleader's profile. - The customer's route refuses to set
maxFollowersbelow the leader's current active follower count. This one allows it. Existing subscriptions are not touched — the number simply stops new followers until the count falls back under it.
Raising maxFollowers past the platform cap does nothing
Eligibility takes the lower of the leader's maxFollowers and the
platform-wide copyTradingMaxFollowersPerLeader setting. Setting a leader to
5,000 while the platform cap sits at its default 1,000 leaves the effective
ceiling at 1,000. To genuinely widen one leader's book you have to raise the
platform setting as well — and that raises it for every leader.
status is not in this endpoint's allow-list. Approving, rejecting,
suspending and reactivating are the dedicated actions, and only those actions
carry the side effects — a suspension pausing every follower, for instance. A
status value sent here is discarded.
What the subscription detail screen shows
/admin/copy-trading/follower/{id} is read-only for settings. It carries Resume,
Pause and Stop, each behind a confirmation, and no edit form. Only the two
destructive ones ask for a reason: Pause and Stop show a reason box and
refuse to submit while it is empty. Resume shows the title, the description
and the confirm button, nothing more — so a resume made from this screen carries
no reason with it.
Its Copy settings card renders the copy mode plus only the sizing parameter
that mode actually uses — a FIXED_RATIO subscription shows its ratio and not
its fixed amount, and vice versa. A stale value left behind by an earlier mode is
deliberately not displayed, because showing both implied both were in play.
PROPORTIONAL shows neither and says the copy is scaled to the allocation.
The risk block underneath always renders all four of maxDailyLoss,
maxPositionSize, stopLossPercent and takeProfitPercent, printing Not set
where there is no limit. "No limit configured" is the single most useful thing a
risk panel can tell you, so it is stated rather than left blank.
Below that, the Allocations card is where the subscription's money physically is: used against allocated, per market, base and quote, with inactive allocations marked. Read it before you stop anything.
What each edit leaves in the audit trail
| Action | Entity type | Action recorded | Actor field | Old value | Reason |
|---|---|---|---|---|---|
| Leader PUT | LEADER |
UPDATE |
adminId |
Recorded | Not captured |
| Subscription PUT | — | — | — | — | — |
| Admin pause | copyTradingFollower |
ADMIN_PAUSE |
userId |
Recorded | In metadata |
| Admin resume | copyTradingFollower |
ADMIN_RESUME |
userId |
In metadata | In metadata if sent — the console never sends one |
| Admin force stop | copyTradingFollower |
ADMIN_FORCE_STOP |
userId |
— | In metadata |
Three things follow from that table.
A subscription edit leaves no trail at all — the audit write is what fails, so there is nothing to find afterwards.
A leader edit is indistinguishable from the leader editing their own profile.
Both write LEADER / UPDATE with the same before-and-after values. The only
discriminator is which actor column is populated: an administrator's edit fills
adminId, the leader's own edit fills userId. The audit endpoint accepts an
adminId filter and an entityId filter, which is how you separate them.
The IP address on these rows is not real. The four subscription routes that
set one read it from a property the request object does not have, so every row
they write records the literal string unknown. Do not treat it as evidence.
Which one to reach for
| You want to | Use | Why |
|---|---|---|
| Correct a mis-set copy mode or sizing parameter | The subscription PUT | Nothing else writes those fields. Expect an error response; verify by re-reading the row |
Correct maxDailyLoss or maxPositionSize |
Neither | No admin route writes them. Only the follower can |
| Hold a subscription during an investigation | Pause | Leaves funds, allocations and open positions exactly as they are, notifies the customer, and is reversible |
| End a subscription | Force stop | It is the terminal action, it takes a reason, and it refuses to run twice. It does not return money either — see below |
| Fix a leader's display name, bio or profit share | The leader PUT | It works, and it is audited properly |
| Change a leader's status | The dedicated action | Status is not writable through the PUT, and only the actions carry the side effects |
Force stop takes a row lock, refuses if the subscription is already stopped,
records the reason — and then writes STOPPED. Funds stay in the per-market
allocations and are released only when those allocations are removed. The full
teardown, with live orders cancelled and unspent capital transferred back to ECO
or SPOT, exists only on the follower's own stop, and even that refuses while a
filled position is still open.
If you have to unwind a subscription and return money, the position has to be closed first and the customer has to run the stop themselves. Plan that conversation before you force stop anything.
Endpoint shapes, permissions and rate limits for the whole surface are in the API reference.