Device Groups
Device Groups let you organize managed devices into logical collections for targeted policy assignment, bulk operations, and fleet segmentation. Most groups belong to one organization and optionally to a site, and they support a parent-child hierarchy for nested grouping. A partner can also create partner-wide groups that span every organization it manages.
Breeze supports two group types:
| Type | Membership | Best for |
|---|---|---|
| Static | Devices are added and removed manually. | Fixed collections such as “Executive Laptops” or “Lobby Kiosks”. |
| Dynamic | Membership is computed automatically from filter rules. Devices enter and leave the group as their attributes change. | Attribute-driven segments such as “Windows Servers with >90% disk” or “Devices offline >7 days”. |
Static vs dynamic groups
Section titled “Static vs dynamic groups”Static groups
Section titled “Static groups”Static groups have a fixed membership list. You add or remove devices explicitly through the API or UI. This is the default group type.
- Devices are added with
addedBy: 'manual'. - Devices can be removed individually.
- No filter conditions are evaluated.
Default group for new devices
Section titled “Default group for new devices”Each organization can name one static group that newly enrolled devices join automatically. Set it under the organization’s settings, Defaults tab, Default device group; the picker lists the organization’s static groups, and None turns it off. A device is added once, when it enrolls; devices already enrolled are not moved. If the group has been deleted or turned dynamic, enrollment still succeeds and the device is simply not added.
The default group must be one of the organization’s own static groups. A partner-wide group cannot be the default, because partner-wide groups are always dynamic.
Dynamic groups
Section titled “Dynamic groups”Dynamic groups use a filterConditions object to define membership rules. When a dynamic group is created or its filter is updated, the system evaluates the filter against all devices in the organization and automatically adds or removes members.
- Devices that match the filter are added with
addedBy: 'dynamic_rule'. - Devices that stop matching are removed automatically – unless they are pinned.
- You cannot manually add or remove devices from a dynamic group. Use pinning instead.
Partner-wide groups
Section titled “Partner-wide groups”A partner-wide group belongs to the partner (MSP), not to one organization. One group definition covers every organization the partner manages, so you write a rule once and use it for all of your customers. Quick Support organizations and the holding organization for unassigned devices are not included.
What partner-wide groups can do
Section titled “What partner-wide groups can do”- Dynamic only. A partner-wide group is always a dynamic group built from a filter. It cannot be static, and devices cannot be added, removed or pinned by hand.
- No site, parent or organization filter. The group has no site or parent group. Its filter can use any field except Organization (
orgId) and Device Group (groupId). The Site field is allowed. - Policy targets. A partner-wide group can be targeted by partner-owned configuration policies (the policies you create for All organizations) and by the Deploy monitor dialog of a partner-wide monitor. An organization’s own policies cannot target it, and other targets (scripts, automations, maintenance windows, software deployments, alert rules and similar) refuse it with an error instead of silently matching no devices.
- Precedence. An assignment to a partner-wide group is a device-group-level assignment. It takes precedence over organization- and site-level assignments of other policies, and a device-level assignment still overrides it.
Who can create and change them
Section titled “Who can create and change them”Only a partner user with access to all organizations can create, edit or delete a partner-wide group. On the Device Groups page, select Create Group and choose All organizations under the owner selector. The selector is offered only when you create a group; an existing group cannot change owner. You can rename a partner-wide group and change its filter later.
Partner users with access to selected organizations can see the group and its filter, but its member counts and member lists include only the organizations they can access, and they cannot change it.
What organization users see
Section titled “What organization users see”Users who sign in to one organization see a partner-wide group’s name with an All orgs badge, and only the devices from their own organization. The filter rules are hidden from them; the card says the group is managed by their service provider. They cannot edit, delete or preview the group.
Finding and checking partner-wide groups
Section titled “Finding and checking partner-wide groups”On the Device Groups page, use the All orgs filter to show only partner-wide groups. Each partner-wide group shows:
- when its membership was last evaluated;
- how many organizations failed to evaluate, if any. Breeze retries those organizations automatically, and saving the group’s filter starts a new evaluation;
- a Show members list with an Organization column.
Membership is updated when devices change and by a periodic background check. Breeze does not promise a fixed update time.
Example: monitor every device that runs a given application
Section titled “Example: monitor every device that runs a given application”Say you want a monitor on every customer device that has PowerChute Business Edition installed.
-
On the Device Groups page, select Create Group, choose All organizations, and name the group, for example “PowerChute agents”.
-
Add the filter condition Has Software Installed
containsPowerChute Business Edition, check the preview, and create the group. -
Open your partner-wide monitor (one created for All organizations) and select Deploy monitor.
-
Choose Create a policy for → a device group, pick “PowerChute agents” under All orgs (partner-wide), and attach.
Breeze creates a partner-owned configuration policy assigned to the group. When a device in any customer organization installs the application, it joins the group and gets the monitor; when the application is removed, the device leaves the group.
To reuse the group for another partner-owned configuration policy, open the policy’s Assignments tab and pick the group under Device groups (all orgs).
Creating a group
Section titled “Creating a group”-
Choose a name (1–255 characters) and a type (
staticordynamic). -
Specify the organization the group belongs to. Optionally scope it to a site. Partner administrators can instead choose All organizations to create a partner-wide group.
-
For dynamic groups, define filter conditions (see Dynamic group rules below).
-
Optionally set a parent group to create a hierarchy. The parent must belong to the same organization.
-
Submit the request. For dynamic groups, membership evaluation runs asynchronously after creation.
curl -X POST /api/v1/groups \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "orgId": "ORG_UUID", "name": "Executive Laptops", "type": "static" }'curl -X POST /api/v1/groups \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "orgId": "ORG_UUID", "name": "High Disk Usage Servers", "type": "dynamic", "filterConditions": { "operator": "AND", "conditions": [ { "field": "osType", "operator": "equals", "value": "linux" }, { "field": "metrics.diskPercent", "operator": "greaterThan", "value": 90 } ] } }'Membership management
Section titled “Membership management”Adding devices to a static group
Section titled “Adding devices to a static group”Send an array of device UUIDs. The API verifies that each device exists and belongs to the same organization as the group. Duplicate memberships are silently skipped.
curl -X POST /api/v1/groups/:id/devices \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["DEVICE_UUID_1", "DEVICE_UUID_2"] }'The response reports how many devices were added and how many were skipped (already members):
{ "data": { "added": 2, "skipped": 0, "total": 5 }}Removing devices from a static group
Section titled “Removing devices from a static group”Remove a single device by its ID:
curl -X DELETE /api/v1/groups/:id/devices/:deviceId \ -H "Authorization: Bearer $TOKEN"You can also remove devices in bulk through the alternate endpoint:
curl -X DELETE /api/v1/devices/groups/:id/members \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["DEVICE_UUID_1", "DEVICE_UUID_2"] }'Listing group members
Section titled “Listing group members”curl /api/v1/groups/:id/devices \ -H "Authorization: Bearer $TOKEN"Each member record includes:
| Field | Description |
|---|---|
deviceId |
UUID of the device |
hostname |
Device hostname |
displayName |
Optional display name |
status |
Current device status (online, offline, etc.) |
osType |
Operating system type |
isPinned |
Whether the device is pinned (dynamic groups only) |
addedAt |
Timestamp when the device joined the group |
addedBy |
How the device was added: manual, dynamic_rule, or policy |
Dynamic groups as execution targets
Section titled “Dynamic groups as execution targets”v0.118.0+. A dynamic group whose rules match on a field the device reports about itself is not used to decide where something runs. Because the device sets these values itself, Breeze skips the group as an execution target. These fields are:
- hostname and display name;
- tags and device role;
- last user;
- agent version and Helper version;
- installed software (installed or not installed), software version and vendor;
- any custom field.
Such a group is skipped as a target of scripts, automations, software policies and update rings. It is also skipped by the automation, software, remote-access, PAM, maintenance, OneDrive and Assist policy features. Patch and backup assignments still resolve through it, and so does every non-execution use: listing members, filters, views and reports.
Each skip is recorded in the audit log with the fields that caused it. Where you target a group interactively, for example from an update ring or a deployment, the response also shows a warning naming the group.
To target these devices, build the group on fields Breeze accepts for targeting, such as organization, site, OS, hardware, network facts or metrics. Or use a static group.
Dynamic group rules
Section titled “Dynamic group rules”Dynamic groups use a filterConditions object that follows a recursive AND/OR structure. Each condition targets a specific device field with an operator and value.
Filter condition structure
Section titled “Filter condition structure”{ "operator": "AND", "conditions": [ { "field": "osType", "operator": "equals", "value": "windows" }, { "operator": "OR", "conditions": [ { "field": "status", "operator": "equals", "value": "offline" }, { "field": "daysSinceLastSeen", "operator": "greaterThan", "value": 7 } ] } ]}Available filter fields
Section titled “Available filter fields”Fields are organized by category. Each field supports a specific set of operators based on its data type.
| Field | Label | Type | Example operators |
|---|---|---|---|
hostname |
Hostname | string | equals, contains, startsWith, matches |
displayName |
Display Name | string | equals, contains, isNull |
status |
Status | enum | equals, in (values: online, offline, maintenance, decommissioned) |
agentVersion |
Agent Version | string | equals, contains, startsWith |
enrolledAt |
Enrolled At | datetime | before, after, withinLast |
lastSeenAt |
Last Seen At | datetime | before, after, withinLast |
tags |
Tags | array | hasAny, hasAll, isEmpty |
| Field | Label | Type | Example operators |
|---|---|---|---|
osType |
OS Type | enum | equals, in (values: windows, macos, linux) |
osVersion |
OS Version | string | equals, contains |
osBuild |
OS Build | string | equals, contains |
architecture |
Architecture | enum | equals, in (values: x64, x86, arm64) |
| Field | Label | Type | Example operators |
|---|---|---|---|
hardware.manufacturer |
Manufacturer | string | equals, contains |
hardware.model |
Model | string | equals, contains |
hardware.serialNumber |
Serial Number | string | equals |
hardware.cpuModel |
CPU Model | string | contains |
hardware.cpuCores |
CPU Cores | number | greaterThan, lessThan, between |
hardware.ramTotalMb |
RAM (MB) | number | greaterThan, lessThan, between |
hardware.diskTotalGb |
Disk Size (GB) | number | greaterThan, lessThan, between |
hardware.gpuModel |
GPU Model | string | contains |
| Field | Label | Type | Example operators |
|---|---|---|---|
network.ipAddress |
IP Address | string | equals, startsWith, contains |
network.publicIp |
Public IP | string | equals, startsWith |
network.macAddress |
MAC Address | string | equals |
metrics.cpuPercent |
CPU % | number | greaterThan, lessThan, between |
metrics.ramPercent |
RAM % | number | greaterThan, lessThan, between |
metrics.diskPercent |
Disk % | number | greaterThan, lessThan, between |
| Field | Label | Type | Example operators |
|---|---|---|---|
software.installed |
Has Software Installed | string or string array | contains, notContains, equals, in, hasAny, hasAll |
software.notInstalled |
Missing Software | string or string array | contains, equals, in, hasAny |
software.version |
Software Version | object (name, version) |
below, atLeast, equals |
software.vendor |
Software Vendor | string | contains, equals |
daysSinceLastSeen |
Days Since Last Seen | number | greaterThan, lessThan |
daysSinceEnrolled |
Days Since Enrolled | number | greaterThan, lessThan |
| Field | Label | Type | Example operators |
|---|---|---|---|
orgId |
Organization | string | equals, in |
siteId |
Site | string | equals, in |
groupId |
Device Group | string | equals, notEquals, in, notIn |
custom.* |
Custom Fields | string | equals, contains, startsWith |
Custom fields use the prefix custom. followed by the field key (e.g., custom.department).
The rule builder lists your organization’s and partner’s custom fields under Custom Fields (v0.123.0+), and the group card shows the field’s name rather than its key, for example “Asset disposition equals Retired”.
Device Group also takes does not equal (notEquals) and is not one of (notIn) from v0.124.0. A device that belongs to no group matches does not equal. Excluding a dynamic group uses that group’s last evaluated membership rather than re-evaluating it on the spot. Saving a dynamic group whose filter references itself – directly, or through a chain of other dynamic groups – is refused.
Operator reference
Section titled “Operator reference”| Operator | Applies to | Description |
|---|---|---|
equals / notEquals |
All types | Exact match or negation |
greaterThan / greaterThanOrEquals |
number, date | Numeric or date comparison |
lessThan / lessThanOrEquals |
number, date | Numeric or date comparison |
below |
software.version | Any install of the named software has a version below the supplied version |
atLeast |
software.version | Any install of the named software has the supplied version or a newer version |
contains / notContains |
string, array | Case-insensitive substring match (ILIKE) |
startsWith / endsWith |
string | Prefix or suffix match |
matches |
string | PostgreSQL regex match (~) |
in / notIn |
string, enum | Value in or not in an array |
hasAny / hasAll |
array | Array overlap or superset check |
isEmpty / isNotEmpty |
array | Array emptiness check |
isNull / isNotNull |
All types | Null check |
before / after |
date, datetime | Date comparison |
between |
number, date | Range check (value: { "from": ..., "to": ... }) |
withinLast / notWithinLast |
date, datetime | Relative time (value: { "amount": 7, "unit": "days" }) |
Software versions take a value such as { "name": "Example App", "version": "10.0.5" }. The name matches exactly, ignoring case. Versions compare numeric segments, so 10.0.10 is newer than 10.0.5, and 1.2 equals 1.2.0. Surrounding whitespace and an optional leading v are removed; suffixes after the leading numeric version are ignored (1.2.3-beta, 1.2.3 (x64) and 1.2.3+meta all equal 1.2.3). Missing versions and strings with no numeric prefix never match. Any matching install is enough, even if another install on the same device has a different version; devices missing the software never match below. Both name and version must be non-empty after trimming (maximum 500 and 100 characters respectively).
Software vendors match any installed software row. equals matches the whole vendor name ignoring case, while contains matches a case-insensitive substring, treating % and _ literally. Inventory changes to versions or vendors trigger dynamic membership re-evaluation even when installed software names stay the same.
Previewing dynamic membership
Section titled “Previewing dynamic membership”Before saving filter changes, you can preview which devices would match:
curl -X POST /api/v1/groups/:id/preview?limit=20 \ -H "Authorization: Bearer $TOKEN"The response includes a total count and a sample of matching devices:
{ "data": { "totalCount": 47, "devices": [ { "id": "...", "hostname": "SRV-PROD-01", "displayName": "Production Server 1", "osType": "linux", "status": "online", "lastSeenAt": "2026-02-18T10:30:00.000Z" } ], "evaluatedAt": "2026-02-18T10:32:00.000Z" }}The limit query parameter controls the number of sample devices returned (1–100, default 10).
Pinning devices in dynamic groups
Section titled “Pinning devices in dynamic groups”Pinning a device to a dynamic group prevents it from being removed when it no longer matches the filter rules. This is useful for devices that must always receive a group’s policies regardless of attribute changes.
# Pin a devicecurl -X POST /api/v1/groups/:id/devices/:deviceId/pin \ -H "Authorization: Bearer $TOKEN"
# Unpin a devicecurl -X DELETE /api/v1/groups/:id/devices/:deviceId/pin \ -H "Authorization: Bearer $TOKEN"When a device is unpinned, the system immediately re-evaluates the filter. If the device no longer matches, it is removed from the group.
Group hierarchy
Section titled “Group hierarchy”Groups support a parent-child relationship through the parentId field. This lets you organize groups into trees:
All Servers ├── Windows Servers │ ├── Domain Controllers │ └── File Servers └── Linux Servers └── Web ServersRules for hierarchy:
- A group cannot be its own parent.
- The parent must belong to the same organization.
- A group with child groups cannot be deleted. Remove or reassign children first.
Group-level policy assignment
Section titled “Group-level policy assignment”Device groups are a valid assignment target for Configuration Policies. In the policy hierarchy, device group sits between site and device:
Partner (lowest priority) └── Organization └── Site └── Device Group └── Device (highest priority)To assign a configuration policy to a device group:
curl -X POST /api/v1/configuration-policies/:policyId/assignments \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "level": "device_group", "targetId": "GROUP_UUID", "priority": 10 }'All devices in the group inherit the policy settings. More specific assignments (at the individual device level) override group-level settings.
Bulk operations on groups
Section titled “Bulk operations on groups”Groups integrate with the deployment system as a target type. When creating a deployment (script execution, patch rollout, software install, or policy push), you can target one or more groups instead of listing individual devices.
The deployment target configuration accepts group IDs:
{ "targetType": "groups", "targetConfig": { "type": "groups", "groupIds": ["GROUP_UUID_1", "GROUP_UUID_2"] }}The deployment system resolves group membership at execution time, so dynamic group changes are reflected automatically.
For quick bulk membership changes on static groups, use the batch endpoints:
# Add multiple devices at oncecurl -X POST /api/v1/devices/groups/:id/members \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["UUID1", "UUID2", "UUID3"] }'
# Remove multiple devices at oncecurl -X DELETE /api/v1/devices/groups/:id/members \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["UUID1", "UUID2"] }'Membership audit log
Section titled “Membership audit log”Every membership change is recorded in the group_membership_log table. You can query the log for a specific group:
curl "/api/v1/groups/:id/membership-log?limit=50&offset=0" \ -H "Authorization: Bearer $TOKEN"Optional filters:
| Parameter | Description |
|---|---|
deviceId |
Filter to a specific device |
action |
Filter by added or removed |
limit |
Number of entries to return (1–500, default 50) |
offset |
Pagination offset (default 0) |
Each log entry includes:
| Field | Description |
|---|---|
id |
Log entry UUID |
groupId |
Group UUID |
deviceId |
Device UUID |
hostname |
Device hostname (joined from devices table) |
displayName |
Device display name |
action |
added or removed |
reason |
Why the change occurred: manual, filter_match, filter_unmatch, pinned, or unpinned |
createdAt |
Timestamp of the change |
API reference
Section titled “API reference”All group endpoints require authentication and one of the following scopes: organization, partner, or system.
Primary group endpoints (/api/v1/groups or /api/v1/device-groups)
Section titled “Primary group endpoints (/api/v1/groups or /api/v1/device-groups)”| Method | Path | Description |
|---|---|---|
GET |
/ |
List groups. Query params: ownerScope (organization, partner, all; default organization), orgId, siteId, type, parentId, search |
POST |
/ |
Create a group. Body: ownerScope (organization or partner; default organization) |
GET |
/:id |
Get a single group |
PATCH |
/:id |
Update a group |
DELETE |
/:id |
Delete a group (fails if it has child groups) |
GET |
/:id/devices |
List devices in a group |
POST |
/:id/devices |
Add devices to a static group |
DELETE |
/:id/devices/:deviceId |
Remove a device from a static group |
POST |
/:id/preview |
Preview dynamic group filter matches. Query: limit |
POST |
/:id/devices/:deviceId/pin |
Pin a device in a dynamic group |
DELETE |
/:id/devices/:deviceId/pin |
Unpin a device from a dynamic group |
GET |
/:id/membership-log |
Query the membership change audit log |
Partner ownership and visibility
Section titled “Partner ownership and visibility”Partner users with access to all organizations can create, edit, and delete their partner’s groups through the primary endpoints. Partner ownership is chosen only on creation; PATCH cannot change it. The API derives partnerId from the authenticated partner user. System users can edit and delete partner groups, but creating one requires a partner user.
Partner groups must be dynamic and supply filterConditions. Creation rejects body orgId, siteId, parentId, deviceIds, and legacy rules; query orgId does not change the owner. Partner groups cannot have a site or parent, use legacy rules, become static, or clear their filter. Filters may use siteId, but orgId and groupId conditions are rejected anywhere in a partner filter. Organization-group filters also cannot reference a partner group.
GET /?ownerScope=all combines organization and partner groups. orgId narrows the organization groups and partner membership counts. siteId or parentId excludes partner groups; type and search apply to both owner kinds. Partner users with selected organization access can read their partner’s definitions, but counts, members, logs, and previews include only accessible organizations. Partner users with no organization access see no partner groups.
Organization users can list and get their own partner’s groups and list their own organization’s members. Their group DTO carries the name and the All orgs badge fields (ownerScope: "partner", orgId: null, partnerId), with filterConditions: null, rules: null, filterFieldsUsed: [], evaluation: null, and no policy. Counts are restricted to their own organization. These users cannot preview, edit, delete, or read membership logs for a partner group. Site-restricted organization users see no partner groups in lists; direct group and member reads return 403 because partner groups have no site.
Group DTOs include orgId, partnerId, ownerScope, and evaluation. Organization groups have partnerId: null and evaluation: null. For partner users and system users, a partner group’s evaluation is:
{ "state": "pending", "evaluatedAt": null, "failedOrgs": 0}state is pending while the evaluated revision trails the filter revision, partial when that revision is evaluated but some organizations failed, and current otherwise. evaluatedAt is an ISO timestamp or null. Create and filter edits enqueue membership evaluation. Partner previews evaluate eligible organizations sequentially within a 5-second budget, exclude Quick Support and holding organizations, and return partial, evaluatedOrgs, and totalOrgs alongside totalCount, devices, and evaluatedAt. Device rows include orgId.
Partner-owned configuration policies may target their own partner’s groups. These assignments use device-group precedence (priority 4), above organization- and site-level assignments. Organization-owned policies cannot target partner groups.
| Refusal | Response |
|---|---|
| Partner-group add/remove devices or pin/unpin | 400 PARTNER_GROUP_READ_ONLY |
Unsupported filter reference (orgId/groupId in a partner filter, or a partner-group reference in an organization filter) |
400 PARTNER_GROUP_FILTER_UNSUPPORTED |
| Partner group used on an unsupported target surface | 400 PARTNER_GROUP_TARGET_UNSUPPORTED |
| Readable partner group edited or deleted by a partner user without all-organization access | 403 |
| Partner group outside the caller’s read scope, or an organization session using a closed partner-group endpoint | 404, identical to an unknown group |
Device-scoped group endpoints (/api/v1/devices/groups)
Section titled “Device-scoped group endpoints (/api/v1/devices/groups)”| Method | Path | Description |
|---|---|---|
GET |
/groups |
List groups for an org. Query: orgId, page, limit |
POST |
/groups |
Create a group |
PATCH |
/groups/:id |
Update a group |
DELETE |
/groups/:id |
Delete a group |
POST |
/groups/:id/members |
Batch add devices to a group |
DELETE |
/groups/:id/members |
Batch remove devices from a group |
Database schema
Section titled “Database schema”The feature uses three tables:
device_groups
| Column | Type | Description |
|---|---|---|
id |
uuid (PK) | Auto-generated group ID |
org_id |
uuid (FK, nullable) | Owning organization; null for a partner-owned group |
partner_id |
uuid (FK, nullable) | Owning partner; exactly one of org_id and partner_id is set |
site_id |
uuid (FK, nullable) | Optional site scope |
name |
varchar(255) | Group name |
type |
enum | static or dynamic |
rules |
jsonb | Legacy rules field |
filter_conditions |
jsonb | Structured filter for dynamic groups |
filter_fields_used |
text[] | Cached list of fields referenced by the filter |
parent_id |
uuid (nullable) | Parent group for hierarchy |
created_at |
timestamp | Creation timestamp |
updated_at |
timestamp | Last update timestamp |
device_group_memberships
| Column | Type | Description |
|---|---|---|
device_id |
uuid (FK, PK) | Device ID |
group_id |
uuid (FK, PK) | Group ID |
is_pinned |
boolean | Whether the device is pinned (default false) |
added_at |
timestamp | When the device was added |
added_by |
enum | manual, dynamic_rule, or policy |
group_membership_log
| Column | Type | Description |
|---|---|---|
id |
uuid (PK) | Log entry ID |
group_id |
uuid (FK) | Group ID |
device_id |
uuid (FK) | Device ID |
action |
enum | added or removed |
reason |
enum | manual, filter_match, filter_unmatch, pinned, or unpinned |
created_at |
timestamp | Timestamp of the change |
Troubleshooting
Section titled “Troubleshooting”Devices not appearing in a dynamic group
Section titled “Devices not appearing in a dynamic group”- Check filter conditions – Use the
POST /api/v1/groups/:id/previewendpoint to verify that the filter matches the expected devices. - Verify organization scope – Dynamic filters only evaluate devices within the group’s
orgId. Devices in other organizations are never matched. - Inspect
filterFieldsUsed– The system caches which fields a filter references. If the cache is stale, the incremental re-evaluation (updateDeviceMembership) may skip the group because it sees no field overlap with the device change. Updating the group’s filter conditions triggers a full re-evaluation and refreshes the cache. - Check the membership log – Query
GET /api/v1/groups/:id/membership-logwith the device ID to see if the device was added and then removed.
Cannot add devices to a group
Section titled “Cannot add devices to a group”- Dynamic groups reject manual additions. You will receive: “Cannot manually add devices to a dynamic group”. Use pinning instead or switch the group type to
static. - Cross-org devices are rejected. All devices must belong to the same organization as the group.
Cannot delete a group
Section titled “Cannot delete a group”- Groups with child groups cannot be deleted. The API returns: “Cannot delete group with child groups”. Delete or reassign children first.
- Deleting a group removes all its membership records automatically.
Pinned devices removed unexpectedly
Section titled “Pinned devices removed unexpectedly”Pinned devices should never be removed by filter re-evaluation. If a pinned device was removed, check the membership log for a reason of unpinned – someone may have unpinned the device before the filter re-evaluation ran. When a device is unpinned, the system immediately checks whether it still matches the filter and removes it if it does not.
Filter validation errors
Section titled “Filter validation errors”When creating or updating a dynamic group with filter conditions, the API validates the filter structure. Common errors:
- “Unknown field” – The field key does not match any known filter field. Check the Available filter fields tables above. Custom fields must use the
custom.prefix. - “Operator not valid for field” – The operator is not supported for the field’s data type. For example,
greaterThanis not valid for enum fields. - “Group must have at least one condition” – A filter condition group cannot be empty.
- “A dynamic group cannot reference itself in its own filter” / “Filter creates a membership cycle between dynamic groups” – A Device Group condition points back at the group being saved, directly or through other dynamic groups (v0.124.0+). Point the condition at a different group.