MngUpdateCustomersAssignedManagerByFilter¶
Sets assigned_manager_id, the CRM owner, for every customer matching the
supplied filter. The filter accepts the same parameter set as
MngGetCustomersByFilter, so a selection previewed
in the customer list can be reused here unchanged.
The confirm flag decides what happens. With confirm: false the command runs
every validation and access check but writes nothing, returning the counters
that describe the outcome. With confirm: true the same run also persists the
change. The response shape is identical in both modes, so a client can render
one screen for the preview and the result.
Use this method for portfolio handover, team rebalancing, and campaign reassignment. To move a single customer, use MngUpdateCustomer. To move customers between desks rather than managers, use MngUpdateCustomersDeskByFilter, which follows the same contract.
SEO¶
Page summary: Bulk-reassign CRM customers to a manager through the ScaleTrade manager TCP API, with a dry-run preview.
Keywords: ScaleTrade customer API, MngUpdateCustomersAssignedManagerByFilter, bulk reassign customers, mass manager assignment, CRM owner reassignment, manager TCP API, dry run bulk update, customer portfolio handover.
Access Control¶
Allowed sessions:
SESSION_MANAGERSESSION_ADMINSESSION_DEALERSESSION_CRM_MANAGERSESSION_CRM_ADMIN
The caller must have CRM access and set_customers. Session type is checked as
explicit set membership, not as a privilege ladder: a type outside the list is
rejected regardless of its numeric value.
Scope is enforced twice. The requested deskFilter is first narrowed to the
desks the caller may see, and then every selected customer is checked
individually against the caller's brand and desk scope. Customers outside that
scope are reported as failures and are never written. Admin scope widens the
brand filter; super admin bypasses the scope and permission checks, but never
the per-customer scope check.
Behavior¶
confirmis mandatory. There is no default, so an incomplete request can never write anything.limitandoffsetare required by the schema but have no effect. Every customer matching the filter is processed, not just one page.orderByis accepted for schema compatibility withMngGetCustomersByFilterbut has no observable effect, because the response carries counters rather than rows.- The target manager is validated once, before the loop. A manager that does not
exist, is disabled, lacks CRM access, or is the hidden recovery manager
id=1rejects the whole request with400 INVALID_MANAGER, rather than producing one identical failure per customer. - Brand match is checked per customer. The target manager must belong to the same brand as the customer; this is the only target check that depends on the individual record.
assigned_manager_id: 0is valid and clears the assignment for every matching customer. Both the target validation and the brand check are skipped in that case, but scope checks still apply.- Customers already assigned to the target manager are counted in
skippedand left untouched, soupdated_timeis not disturbed and the audit trail stays clean. - Application is partial. A failure on one customer does not stop the run; it is
recorded in
failuresand processing continues. - Both modes perform identical checks. The dry run differs only in that it does not write, so the preview reports the real outcome.
- Every call is written to the audit log with the caller id, mode, target manager id, and all four counters.
Order Of Operations, Per Customer¶
- Brand and desk scope check. A failure yields
PERMISSION_DENIED. - Already assigned to the target manager: counted in
skipped, no further work. - Target manager brand compared with the customer
brand. A mismatch yieldsINVALID_MANAGER. - With
confirm: false, processing stops here. - Assignment written through the standard customer update path. Storage errors are recorded as failures.
Request¶
Dry run, report what would change:
{
"command": "MngUpdateCustomersAssignedManagerByFilter",
"extID": "1",
"data": {
"assigned_manager_id": 10,
"confirm": false,
"filter": {
"deskFilter": "*",
"limit": 100,
"offset": 0,
"where": [
["lifecycle_stage", "=", 0],
["brand", "=", "default"]
]
}
}
}
Apply, the same request with the flag flipped:
{
"command": "MngUpdateCustomersAssignedManagerByFilter",
"extID": "2",
"data": {
"assigned_manager_id": 10,
"confirm": true,
"filter": {
"deskFilter": "*",
"limit": 100,
"offset": 0,
"where": [
["lifecycle_stage", "=", 0],
["brand", "=", "default"]
]
}
}
}
Clear the assignment for every matching customer:
{
"command": "MngUpdateCustomersAssignedManagerByFilter",
"extID": "3",
"data": {
"assigned_manager_id": 0,
"confirm": true,
"filter": {
"deskFilter": "DESK_EU*",
"limit": 100,
"offset": 0
}
}
}
Request Data¶
Top level:
| Field | Type | Required | Description |
|---|---|---|---|
assigned_manager_id |
int | Yes | Target CRM manager id, minimum 0. 0 clears the assignment |
confirm |
bool | Yes | false reports what would change without writing. true applies the change |
filter |
object | Yes | Customer selection, same parameter set as MngGetCustomersByFilter |
filter object:
| Field | Type | Required | Description |
|---|---|---|---|
deskFilter |
string | Yes | Desk wildcard mask, for example *, DESK_*, DESK_EU*,!DESK_EU_TEST. Narrowed to the desks visible to the caller |
limit |
int | Yes | From 1 to 50000. Required by the schema, does not limit the update |
offset |
int | Yes | Minimum 0. Required by the schema, does not limit the update |
where |
array | No | [[field, operator, value], ...], all conditions must match |
orWhere |
array | No | OR comparison group; at least one condition must match. Other filters stay mandatory |
whereNot |
array | No | [[field, value], ...] |
whereIn |
array | No | [[field, [values...]], ...] |
whereNotIn |
array | No | [[field, [values...]], ...] |
whereBetween |
array | No | [[field, [from, to]], ...] |
whereNotBetween |
array | No | [[field, [from, to]], ...] |
orderBy |
array | No | Example: [["created_time", "desc"]]. Accepted for compatibility, has no observable effect |
Supported filter fields are the customer identity, contact, CRM, acquisition,
compliance, and lifecycle fields, the same set as
MngGetCustomersByFilter. String comparisons
support =, !=, and like, where like is a case-insensitive substring
match. Common filter semantics, including grouped orWhere, are described in
Table filter syntax; desk masks are described in
Desks TCP API.
Response Data¶
Counters only, with no per-customer rows, so the payload stays the same size whether four customers are affected or forty thousand. The key set is identical in both modes.
Dry run:
{
"confirm": false,
"assigned_manager_id": 10,
"matched": 1240,
"updated": 0,
"skipped": 338,
"failed": 2,
"failures": [
{"customer_id": 1099, "error": "PERMISSION_DENIED"},
{
"customer_id": 1101,
"error": "INVALID_MANAGER",
"message": "Manager belongs to another brand"
}
]
}
Applied:
{
"confirm": true,
"assigned_manager_id": 10,
"matched": 1240,
"updated": 900,
"skipped": 338,
"failed": 2,
"failures": [
{"customer_id": 1099, "error": "PERMISSION_DENIED"},
{
"customer_id": 1101,
"error": "INVALID_MANAGER",
"message": "Manager belongs to another brand"
}
]
}
| Field | Type | Description |
|---|---|---|
confirm |
bool | Echo of the request flag, so the client can tell a preview from an applied run |
assigned_manager_id |
int | Target manager id, echoed back. 0 means the assignment was cleared |
matched |
int | Customers selected by the filter, brand scope, and desk mask |
updated |
int | Customers actually reassigned. Always 0 when confirm is false |
skipped |
int | Customers already assigned to the target manager. Reported in both modes |
failed |
int | Customers that did not pass the per-customer checks. Reported in both modes |
failures |
array | One entry per failure with customer_id, error, and an optional message. Present only when non-empty |
Reading The Counters¶
After an applied run the identity matched = updated + skipped + failed holds.
On a dry run updated is 0 by definition, and the number of customers that
would be reassigned is matched - skipped - failed.
Response Statuses¶
| Status | Condition |
|---|---|
200 |
Dry run. Always, since nothing was meant to change |
200 |
Applied, at least one customer updated, no failures |
207 |
Applied, at least one customer updated, some failed |
400 |
Schema validation failed, or the target manager is unusable |
409 |
Applied, no customer updated |
Repeating A Completed Run Returns 409¶
Once every customer is assigned to the target manager, running the same request
again yields updated: 0 with skipped equal to matched and an empty failure
list, and therefore 409. This is an idempotent no-op, not a fault. Clients
must distinguish the two cases by the counters: skipped == matched with no
failures means the work was already done, while a non-zero failed means
nothing could be written.
Errors¶
Request-level:
| Error | Status | Description |
|---|---|---|
INVALID_DATA |
400 |
Schema validation failed. The message field names the offending parameter |
INVALID_MANAGER |
400 |
The target manager does not exist, is disabled, has no CRM access, or is the hidden recovery manager. Message: Manager does not exist, is disabled, or has no CRM access |
PERMISSION_DENIED_ACCESS |
401 |
Session type is not in the allowed list |
| — | 403 |
Caller context could not be resolved: not found, disabled, missing CRM scope, or missing set_customers |
Per-customer, reported inside failures:
| Error | Description |
|---|---|
PERMISSION_DENIED |
The customer is outside the caller's brand or desk scope |
INVALID_MANAGER |
The target manager belongs to another brand than this customer |
RET_ERROR |
Storage or internal error while writing the customer |
RET_ERR_NOTFOUND |
The customer disappeared between selection and write |
INVALID_MANAGER Appears At Two Levels And Means Different Things¶
At request level with status 400 the manager itself is unusable, and nothing
was processed. Inside failures the manager is fine but belongs to a different
brand than that particular customer. A selection spanning several brands will
therefore succeed for some customers and fail for others with the same target.
Narrow the filter by brand, or run the command once per brand.
See also MngUpdateCustomersDeskByFilter for the same contract applied to desks, MngGetCustomersByFilter for the selection, filtering, and scope rules, and MngDeleteAccountsList for the same bulk result shape.