Skip to content

MngUpdateCustomersDeskByFilter

Reassigns the CRM desk 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 desk rebalancing, team handover, and campaign reassignment. To move a single customer, use MngUpdateCustomer instead.

SEO

Page summary: Bulk-reassign CRM customers to a desk through the ScaleTrade manager TCP API, with a dry-run preview.

Keywords: ScaleTrade customer API, MngUpdateCustomersDeskByFilter, bulk update customers, mass desk assignment, CRM desk reassignment, manager TCP API, dry run bulk update, customer desk transfer.

Access Control

Allowed sessions:

  • SESSION_MANAGER
  • SESSION_ADMIN
  • SESSION_DEALER
  • SESSION_CRM_MANAGER
  • SESSION_CRM_ADMIN

The manager 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 manager may see, and then every selected customer is checked individually against the manager 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

  • confirm is mandatory. There is no default, so an incomplete request can never write anything.
  • limit and offset are required by the schema but have no effect. Every customer matching the filter is processed, not just one page.
  • orderBy is accepted for schema compatibility with MngGetCustomersByFilter but has no observable effect, because the response carries counters rather than rows.
  • desk is normalized to uppercase, the same as in MngAddCustomer and MngUpdateCustomer.
  • An empty desk is valid and clears the desk assignment for every matching customer.
  • The target desk is validated per customer against that customer's brand. A desk belongs to exactly one brand, so the same target can be valid for some customers of a mixed selection and invalid for others.
  • Customers already on the target desk are counted in skipped and left untouched, so updated_time is 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 failures and 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 manager id, mode, target desk, and all four counters.

Order Of Operations, Per Customer

  1. Brand and desk scope check. A failure yields PERMISSION_DENIED.
  2. Already on the target desk: counted in skipped, no further work.
  3. Target desk validated against the customer brand. A failure yields INVALID_DESK.
  4. With confirm: false, processing stops here.
  5. Desk written through the standard customer update path. Storage errors are recorded as failures.

Request

Dry run, report what would change:

{
  "command": "MngUpdateCustomersDeskByFilter",
  "extID": "1",
  "data": {
    "desk": "DESK_RETENTION",
    "confirm": false,
    "filter": {
      "deskFilter": "*",
      "limit": 100,
      "offset": 0,
      "where": [
        ["lifecycle_stage", "=", 0],
        ["brand", "=", "default"]
      ]
    }
  }
}

Apply, the same request with the flag flipped:

{
  "command": "MngUpdateCustomersDeskByFilter",
  "extID": "2",
  "data": {
    "desk": "DESK_RETENTION",
    "confirm": true,
    "filter": {
      "deskFilter": "*",
      "limit": 100,
      "offset": 0,
      "where": [
        ["lifecycle_stage", "=", 0],
        ["brand", "=", "default"]
      ]
    }
  }
}

Request Data

Top level:

Field Type Required Description
desk string Yes Target desk code, up to 64 characters. Normalized to uppercase. An empty string clears the desk 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 manager
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,
  "desk": "DESK_RETENTION",
  "matched": 1240,
  "updated": 0,
  "skipped": 338,
  "failed": 2,
  "failures": [
    {"customer_id": 1099, "error": "PERMISSION_DENIED"},
    {
      "customer_id": 1101,
      "error": "INVALID_DESK",
      "message": "Desk does not exist, is disabled, archived, or belongs to another brand"
    }
  ]
}

Applied:

{
  "confirm": true,
  "desk": "DESK_RETENTION",
  "matched": 1240,
  "updated": 900,
  "skipped": 338,
  "failed": 2,
  "failures": [
    {"customer_id": 1099, "error": "PERMISSION_DENIED"},
    {
      "customer_id": 1101,
      "error": "INVALID_DESK",
      "message": "Desk does not exist, is disabled, archived, or 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
desk string Target desk code after normalization
matched int Customers selected by the filter, brand scope, and desk mask
updated int Customers actually moved. Always 0 when confirm is false
skipped int Customers already on the target desk. 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 move 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
409 Applied, no customer updated

Repeating A Completed Run Returns 409

Once every customer sits on the target desk, 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
PERMISSION_DENIED_ACCESS 401 Session type is not in the allowed list
403 Manager 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 manager brand or desk scope
INVALID_DESK The target desk does not exist for that customer's brand, is disabled, or is archived
RET_ERROR Storage or internal error while writing the customer
RET_ERR_NOTFOUND The customer disappeared between selection and write

A Widespread INVALID_DESK Usually Means A Brand Mismatch

A desk is keyed by brand and code together, and the lookup uses the customer's brand. If the target desk is registered under one brand while the selected customers belong to another, or have no brand set, every one of them fails with INVALID_DESK even though the desk exists and is active. Either narrow the filter to the desk's brand, or register the desk in each brand that needs it.

See also MngGetCustomersByFilter for the selection, filtering, and scope rules, and MngDeleteAccountsList for the same bulk result shape.