Skip to content

MngUpdateCustomersByFilter

Writes an arbitrary combination of editable fields to every customer matching the supplied filter. Only the fields present in the fields object are written; everything else on the record is left untouched.

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.

This is the general form of a family that also includes three narrow commands: MngUpdateCustomersDeskByFilter, MngUpdateCustomersAssignedManagerByFilter, and MngUpdateCustomersStatusesByFilter. All four share the same filter, response shape, and status codes. The narrow commands remain the better choice when a UI edits exactly one thing, because their request is simpler and their intent is explicit in the audit log.

The fields those narrow commands handle, desk, assigned_manager_id, and the three statuses, are validated here by the same shared logic they use, not by calling them. A request carrying all five therefore performs one selection and one database write per customer, rather than five.

SEO

Page summary: Bulk-edit any combination of CRM customer fields through the ScaleTrade manager TCP API, with a dry-run preview.

Keywords: ScaleTrade customer API, MngUpdateCustomersByFilter, bulk update customers, mass edit customer fields, CRM bulk edit, manager TCP API, dry run bulk update, customer field whitelist.

Access Control

Allowed sessions:

  • SESSION_MANAGER
  • SESSION_ADMIN
  • SESSION_DEALER
  • SESSION_CRM_MANAGER
  • SESSION_CRM_ADMIN

The caller must have CRM access and set_customers. Session type is checked as explicit set membership, not as a privilege ladder.

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

  • confirm is mandatory. There is no default, so an incomplete request can never write anything.
  • At least one field must be present in fields. An empty object is rejected with 400.
  • Unsupported keys are rejected, not ignored. Any key outside the whitelist fails the whole request with 400 naming the offending field. In a bulk edit a silently dropped field is worse than a refusal.
  • Only the fields present are written. Everything else on the customer record stays as it was.
  • A value of 0 or "" clears a target field. That applies to desk, assigned_manager_id, and the three statuses; clearing bypasses dictionary and brand checks.
  • All brand-bound targets must share one brand. The assigned manager and the statuses are resolved up front and must belong to the same brand, otherwise no customer could satisfy the set and the request is rejected with 400.
  • The set is applied whole or not at all. If any target check fails for a customer, none of the fields are written for that customer.
  • Customers where every requested field already holds the requested value are counted in skipped.
  • limit and offset are required by the schema but have no effect. Every customer matching the filter is processed.
  • orderBy is accepted for schema compatibility but has no observable effect.
  • Application is partial across customers. A failure on one is recorded in failures and the run continues.
  • Every call is written to the audit log with the caller id, mode, the list of field names, and all four counters.

Order Of Operations

  1. Schema validation, then every key in fields is matched against the whitelist.
  2. Brand-bound targets are resolved once: the manager must exist, be enabled, and hold CRM access; each non-zero status must exist with the matching type; all of them must share one brand. Any failure rejects the request with 400.
  3. Per customer: brand and desk scope check. A failure yields PERMISSION_DENIED.
  4. Per customer: every requested field already equal, counted in skipped.
  5. Per customer: the targets' brand compared with the customer brand. A mismatch yields INVALID_MANAGER.
  6. Per customer: desk checked against that customer's brand. A failure yields INVALID_DESK.
  7. With confirm: false, processing stops here.
  8. All requested fields written together in one update.

Why Desk Is Checked Per Customer While The Others Are Checked Once

A desk is keyed by brand and code together, so its brand cannot be derived from the code alone: the lookup needs a brand to start from. Managers and statuses are keyed by id, so their brand is known up front. This is why a bad manager id fails the whole request immediately, while a desk that is missing in one brand only fails the customers of that brand.

Request

Dry run, mixed field types in one call:

{
  "command": "MngUpdateCustomersByFilter",
  "extID": "1",
  "data": {
    "fields": {
      "desk": "DESK_EU",
      "assigned_manager_id": 10,
      "risk_status": 2,
      "segment": "vip",
      "marketing_allowed": 1
    },
    "confirm": false,
    "filter": {
      "deskFilter": "*",
      "limit": 100,
      "offset": 0,
      "where": [
        ["lifecycle_stage", "=", 0]
      ]
    }
  }
}

Apply, a single simple field:

{
  "command": "MngUpdateCustomersByFilter",
  "extID": "2",
  "data": {
    "fields": {
      "lifecycle_stage": 1
    },
    "confirm": true,
    "filter": {
      "deskFilter": "DESK_EU*",
      "limit": 100,
      "offset": 0
    }
  }
}

Clear the desk and the CRM owner together:

{
  "command": "MngUpdateCustomersByFilter",
  "extID": "3",
  "data": {
    "fields": {
      "desk": "",
      "assigned_manager_id": 0
    },
    "confirm": true,
    "filter": {
      "deskFilter": "*",
      "limit": 100,
      "offset": 0
    }
  }
}

Request Data

Top level:

Field Type Required Description
fields object Yes Fields to write. At least one, all keys must be in the whitelist
confirm bool Yes false reports what would change without writing. true applies
filter object Yes Customer selection, same parameter set as MngGetCustomersByFilter

The fields object is described in Editable Fields, the filter object in Filter.

Editable Fields

Thirty-three fields are accepted. Five are brand-bound targets with dictionary validation; the remaining twenty-eight are plain scalars validated by type and range only.

Brand-Bound Targets

Field Type Rule Clearing value
desk string Up to 64 characters, uppercased. Must exist, be enabled, and be active in the customer's brand ""
assigned_manager_id int 0 or greater. Manager must exist, be enabled, hold CRM access, and not be the recovery manager 0
sales_status int 0 or greater. Dictionary id of type SALES 0
risk_status int 0 or greater. Dictionary id of type RISK 0
finance_status int 0 or greater. Dictionary id of type FINANCE 0

Status types and the dictionary record are described in Customer Status Dictionary; read the dictionary with MngGetCustomerStatusesByFilter.

Integer Fields

Field Range Meaning
enable 0..1 Customer enabled
status 0..2 0 active, 1 blocked, 2 archived
type 0..1 0 individual, 1 business
lifecycle_stage 0..6 0 lead through 6 dormant
deposit_allowed 0..1 Deposits allowed
withdrawal_allowed 0..1 Withdrawals allowed
manager_id 0 or greater Legacy responsible manager
source_type 0..4 0 manual, 1 web, 2 import, 3 affiliate, 4 system
pep_status 0..2 0 unknown, 1 clear, 2 positive
sanctions_status 0..3 0 unknown, 1 clear, 2 positive, 3 review
risk_level 0..2 0 low, 1 medium, 2 high
aml_status 0..3 0 pending, 1 review, 2 approved, 3 rejected
marketing_allowed 0..1 Marketing consent
email_verified 0..1 Email verified
phone_verified 0..1 Phone verified

Text Fields

Field Max length Meaning
crm_stage 255 CRM pipeline stage
segment 255 Customer segment
lead_source 255 Acquisition source
campaign 255 Campaign identifier
affiliate_id 255 Affiliate reference
introducer_id 255 Introducer or IB reference
preferred_language 64 Language preference
timezone 64 Timezone label
citizenship 64 Citizenship country code
country_of_residence 64 Residence country code

Timestamp Fields

Field Type Meaning
next_contact_time int64, 0 or greater Next planned CRM contact
last_contact_time int64, 0 or greater Last CRM contact
first_contact_time int64, 0 or greater First CRM contact

Fields Deliberately Excluded

Field Reason
customer_id Record identity
password, otp_secret, enable_otp Authentication material, never acceptable in bulk
email, phone Unique per record; a mass write would collide. Also gated by set_customer_contacts
first_name, last_name, middle_name, full_name, birth_date, tax_id, national_id Personal data, meaningless to set identically across many people
external_id, old_id, import_batch_id Per-record identifiers
created_time, updated_time, created_by, updated_by System-maintained
conversion_time, archive_time, last_login_time Derived from events, not set by hand
brand Changing the brand would orphan the desk, manager, and status references. Needs a dedicated command that migrates them
tags_json, meta_json Free-form JSON; a mass write would erase per-customer data
kyc_status A fourth dictionary status, excluded by product decision

Filter

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
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, 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.

Applied:

{
  "confirm": true,
  "fields": {
    "desk": "DESK_EU",
    "assigned_manager_id": 10,
    "risk_status": 2,
    "segment": "vip",
    "marketing_allowed": 1
  },
  "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"
    }
  ]
}

A dry run returns the same keys with confirm: false and updated: 0.

Field Type Description
confirm bool Echo of the request flag
fields object Echo of the applied fields, exactly as supplied
matched int Customers selected by the filter, brand scope, and desk mask
updated int Customers actually written. Always 0 when confirm is false
skipped int Customers where every requested field already held the requested value
failed int Customers that did not pass the per-customer checks
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 written is matched - skipped - failed.

Response Statuses

Status Condition
200 Dry run. Always, since nothing was meant to change
200 Applied, at least one customer written, no failures
207 Applied, at least one customer written, some failed
400 Schema validation failed, fields was empty or carried an unsupported key, or a brand-bound target is unusable
409 Applied, no customer written

Repeating A Completed Run Returns 409

Once every customer already holds the requested values, 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
INVALID_DATA 400 fields carried no editable field. Message: fields must contain at least one editable field
INVALID_DATA 400 fields carried a key outside the whitelist. Message: fields contains an unsupported field: <name>
INVALID_MANAGER 400 The target manager does not exist, is disabled, has no CRM access, or is the recovery manager
INVALID_STATUS 400 A referenced status is missing or has the wrong type, and the message names the field; or the manager and statuses belong to different brands
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 manager or statuses belong to a different brand than this customer. Message: Target manager or statuses belong to another brand
INVALID_DESK The target desk does not exist for this 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

Brand Is The Usual Cause Of Mass Failures

Desks, managers, and statuses all belong to exactly one brand, and so does every customer. A selection spanning several brands will therefore fail for everyone outside the targets' brand, even though the targets themselves are perfectly valid. Narrow the filter with ["brand", "=", "..."], or run the command once per brand.

See also MngUpdateCustomersDeskByFilter, MngUpdateCustomersAssignedManagerByFilter, and MngUpdateCustomersStatusesByFilter for the narrow commands of the same family, MngUpdateCustomer for a single record, MngGetCustomersByFilter for the selection, filtering, and scope rules, and MngDeleteAccountsList for the same bulk result shape.