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.

8 min readUpdated 6 August 2026leaders, subscriptions, editing, validation, audit

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

Writes subscription columns directly. No teardown, no notification

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 newValueafter 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

Writes leader profile columns directly. No validation in the route

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 a PENDING, REJECTED or SUSPENDED leader's profile.
  • The customer's route refuses to set maxFollowers below 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.