Skip to content

MngGetCustomersByFilter

Returns a paginated customer list.

email and phone are masked by default. A caller holding see_customer_contacts can request open values with contacts: "full". Email domain remains visible while masked, and filtering always works against full stored values in both modes.

Access Control

Requires CRM access and see_customers. The requested deskFilter is intersected with the manager desk scope unless the manager has see_all_customers or admin scope. Customer brand must match manager brand when brand scope is set.

contacts: "full" additionally requires see_customer_contacts, the same permission that governs MngGetCustomerContacts and full-contact export. It is checked once, before any customer is selected, so an unauthorised request returns immediately. Row-level visibility is unaffected by the flag: customers outside the caller's brand or desk scope are filtered out in both modes.

Request Parameters

Name Type Required Description
limit int Yes Maximum rows, from 1 to 50000
offset int Yes Pagination offset
deskFilter string Yes Desk wildcard mask, for example *, DESK_*, DESK_EU*,!DESK_EU_TEST
contacts string No masked or full, default masked. full returns email and phone unmasked and requires see_customer_contacts
where array No [[field, operator, value], ...]
orWhere array No OR comparison group in [[field, operator, value], ...] format; 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"]]

Request

{
  "command": "MngGetCustomersByFilter",
  "extID": "1",
  "data": {
    "limit": 50,
    "offset": 0,
    "deskFilter": "DESK_*",
    "where": [
      ["email", "like", "example.com"]
    ],
    "orderBy": [
      ["created_time", "desc"]
    ]
  }
}

Asking for open contacts:

{
  "command": "MngGetCustomersByFilter",
  "extID": "2",
  "data": {
    "limit": 100,
    "offset": 0,
    "deskFilter": "*",
    "contacts": "full",
    "where": [
      ["lifecycle_stage", "=", 2]
    ]
  }
}

Response Data

{
  "structure": [
    "customer_id",
    "external_id",
    "type",
    "status",
    "lifecycle_stage",
    "enable",
    "enable_otp",
    "deposit_allowed",
    "withdrawal_allowed",
    "first_name",
    "last_name",
    "middle_name",
    "full_name",
    "birth_date",
    "citizenship",
    "country_of_residence",
    "email",
    "phone",
    "preferred_language",
    "timezone",
    "brand",
    "desk",
    "manager_id",
    "lead_source",
    "campaign",
    "affiliate_id",
    "introducer_id",
    "source_type",
    "landing_url",
    "referrer_url",
    "utm_source",
    "utm_medium",
    "utm_campaign",
    "utm_content",
    "utm_term",
    "import_batch_id",
    "old_id",
    "assigned_manager_id",
    "crm_stage",
    "segment",
    "tags_json",
    "email_verified",
    "phone_verified",
    "marketing_allowed",
    "tax_id",
    "national_id",
    "pep_status",
    "sanctions_status",
    "risk_level",
    "aml_status",
    "kyc_status",
    "sales_status",
    "risk_status",
    "finance_status",
    "kyc_updated_time",
    "aml_updated_time",
    "last_contact_time",
    "first_contact_time",
    "next_contact_time",
    "conversion_time",
    "last_login_time",
    "archive_time",
    "created_time",
    "updated_time",
    "created_by",
    "updated_by",
    "meta_json"
  ],
  "rows": [
    [
      1,
      "",
      0,
      0,
      0,
      1,
      0,
      1,
      1,
      "John",
      "Smith",
      "",
      "John Smith",
      0,
      "",
      "",
      0,
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "cl***@example.com",
      "+35***000",
      "",
      "",
      "default",
      0,
      10,
      "",
      "",
      "",
      "",
      10,
      "qualified",
      "",
      "",
      0,
      0,
      0,
      0,
      "",
      "",
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      1777700000,
      1777700000,
      10,
      10,
      ""
    ]
  ],
  "count": 1
}

The example above is a masked response. With contacts: "full" the same row returns [email protected] and +35700000000 in those two positions, and nothing else in the response differs.

Contact Fields

The flag changes two values, never the shape of the response. structure keeps the same columns, with the same names in the same order, in both modes, and count, paging, filtering, and ordering are untouched. Omitting contacts is identical to sending masked.

Column masked full
email iv***@example.com [email protected]
phone +79***567 +79161234567

Masking keeps the first two characters of the email local part and the first and last three digits of the phone. Empty values stay empty in both modes.

Any value other than masked and full is rejected by schema validation with 400. MngExportCustomersByFilter additionally accepts the integer alias mask_contacts; that alias is not available here, the list accepts contacts only.

Who Can Request Open Contacts

A caller qualifies when either of these holds:

  • they are a super administrator, or
  • they are an enabled manager session with the access_crm scope and see_customer_contacts = 1.

The predicate short-circuits on super administrator status, so such a caller may request full even when see_customer_contacts is 0 in their record. Anything reproducing this check client-side must reproduce the whole predicate rather than read the raw permission field, or the option disappears for administrators who are in fact entitled to it.

Client Integration

Because an unauthorised full is a hard 403 rather than a downgrade to masked values, the client must know the caller's permission before it sends the request. A stale permission cache produces an empty screen, not a degraded one.

Sending full is best wired to a deliberate user action, a "show contacts" toggle, rather than sent automatically whenever the permission exists. It keeps personal data off screens that do not need it, and it makes the audit record correspond to something a person actually did.

Note that MngGetCustomer unmasks automatically for anyone holding the permission, with no parameter at all, while this command requires the parameter. Until a client starts sending contacts: "full", a manager with the permission sees an open contact on the customer card and a masked one in the list. That is expected, not a defect.

Audit

Every request that actually reveals contacts is written to the audit log with the caller id, the number of rows returned, and the desk filter used. Requests without contacts: "full" are not logged: this is the most frequently called command in the CRM, and auditing every call would flood the log. The audit record therefore marks a disclosure, not a query.

How Contacts Open Across Commands

Command Scale How contacts open Permission
MngGetCustomer One customer Automatically, no parameter see_customer_contacts
MngGetCustomerContacts One customer Always, that is its purpose see_customer_contacts
MngExportCustomersByFilter Bulk, to a file contacts: "full" export_customers plus see_customer_contacts
MngGetCustomersByFilter Bulk, on screen contacts: "full" see_customers plus see_customer_contacts

Errors

Error Status Condition
INVALID_DATA 400 contacts holds a value other than masked or full
RET_NOT_ENOUGH_RIGHTS 403 full requested without see_customer_contacts

An unauthorised full fails the whole request: no structure, no rows, and no count. The list does not degrade to masked values. This mirrors MngExportCustomersByFilter, where the same request is refused outright.