Skip to content

Filters & Search

Overview

Breeze RMM provides two complementary systems for locating and segmenting devices:

  • Saved Filters — Persistent, reusable rule sets that match devices based on field conditions. Filters are scoped to an organization and support nested AND/OR logic across dozens of device fields (core attributes, hardware, network, metrics, software, hierarchy, custom fields, and computed values). Saved filters are also used as targeting criteria by the Deployment engine and Dynamic Device Groups.
  • Global Search — A fast, multi-category search endpoint that queries devices, scripts, alerts, and settings pages simultaneously using case-insensitive substring matching.

Both systems enforce multi-tenant access control. Organization-scoped users see only their own resources; partner-scoped users see resources across their accessible organizations.


Saved Filters

Saved filters let you define a set of conditions that dynamically match devices in your fleet. Once created, a filter can be previewed (to see which devices currently match), shared with other users in the same organization, and referenced by deployments and dynamic groups.

Creating a Saved Filter

  1. Choose a name and optional description. The name can be up to 200 characters; the description up to 1,000 characters.

  2. Define filter conditions. Build a condition group using the AND/OR operator and one or more individual conditions or nested groups. See Filter Rule Syntax below for the full specification.

  3. POST the filter to the API.

    Terminal window
    curl -X POST https://breeze.yourdomain.com/api/v1/filters \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
    "name": "Stale Windows Devices",
    "description": "Windows devices not seen in 7+ days",
    "conditions": {
    "operator": "AND",
    "conditions": [
    { "field": "osType", "operator": "equals", "value": "windows" },
    { "field": "daysSinceLastSeen", "operator": "greaterThan", "value": 7 }
    ]
    }
    }'
  4. Receive the saved filter response. The API returns the full saved filter object, including the generated id, createdBy, and timestamps.

Sharing Filters

Saved filters are scoped to the organization they belong to. Any authenticated user with access to that organization can list, view, and preview the filter. There is no separate sharing or visibility toggle — all filters within an organization are visible to all users who have access to that organization.

Partner-scoped users can list filters across all of their accessible organizations in a single request. The GET /filters endpoint returns filters from every organization the caller can access, sorted by creation date (newest first).

Filter Rules

Each saved filter stores its conditions as a JSON structure in the conditions column. The top-level object must be a condition group with an operator (AND or OR) and an array of conditions. Each element in the array is either:

  • A single condition — an object with field, operator, and optionally value.
  • A nested condition group — another group with its own operator and conditions, allowing arbitrarily deep nesting.

Example: devices that are Windows and either have high CPU or are low on disk:

{
"operator": "AND",
"conditions": [
{ "field": "osType", "operator": "equals", "value": "windows" },
{
"operator": "OR",
"conditions": [
{ "field": "metrics.cpuPercent", "operator": "greaterThan", "value": 90 },
{ "field": "metrics.diskPercent", "operator": "greaterThan", "value": 95 }
]
}
]
}

Previewing Filter Results

You can preview which devices currently match a filter — either an ad-hoc set of conditions or a saved filter.

Ad-hoc preview (no saved filter required):

Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/filters/preview \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"conditions": {
"operator": "AND",
"conditions": [
{ "field": "status", "operator": "equals", "value": "offline" }
]
},
"limit": 5
}'

Saved filter preview:

Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/filters/<filter-id>/preview?limit=10 \
-H "Authorization: Bearer $TOKEN"

Both return the same response shape:

{
"data": {
"totalCount": 42,
"devices": [
{
"id": "uuid",
"hostname": "ws-01",
"displayName": "Workstation 01",
"osType": "windows",
"status": "offline",
"lastSeenAt": "2026-02-10T08:00:00.000Z"
}
],
"evaluatedAt": "2026-02-18T12:00:00.000Z"
}
}

The totalCount reflects the total number of matching devices across all accessible organizations. The devices array is capped by the limit parameter (maximum 100, default 10).


The global search endpoint (GET /search) performs a case-insensitive substring match across multiple resource categories in a single request.

Search queries are matched using SQL ILIKE against the following fields:

CategoryFields searched
Deviceshostname, displayName
Scriptsname, description
Alertstitle, message
Settingstitle, description (static entries)

Results are returned in a flat array, each item tagged with its type (devices, scripts, alerts, or settings).

Terminal window
curl "https://breeze.yourdomain.com/api/v1/search?q=workstation&limit=20" \
-H "Authorization: Bearer $TOKEN"
{
"results": [
{
"id": "dev-uuid",
"type": "devices",
"title": "Workstation 01",
"description": "online"
},
{
"id": "script-uuid",
"type": "scripts",
"title": "Workstation Setup",
"description": "Initial workstation provisioning script"
},
{
"id": "settings-users",
"type": "settings",
"title": "User management",
"description": "Manage users and roles",
"href": "/settings/users"
}
]
}

Advanced Filtering

For advanced device filtering beyond simple substring search, use the Saved Filters system described above. Saved filters support structured conditions with typed operators, nested logic, and field-specific matching across hardware specs, metrics, software inventory, network details, and more.

The two systems complement each other:

CapabilitySearch (/search)Filters (/filters)
Multi-category resultsYes (devices, scripts, alerts, settings)No (devices only)
Substring matchingYesYes (via contains operator)
Typed field operatorsNoYes (30+ operators)
Nested AND/OR logicNoYes
Hardware/metrics/network fieldsNoYes
Saveable and reusableNoYes
Deployment targetingNoYes

Search Parameters

ParameterTypeRequiredDefaultConstraints
qstringYes1-100 characters, trimmed
limitnumberNo201-50

Filter Rule Syntax

Condition Group

The root of every filter must be a condition group:

{
operator: "AND" | "OR",
conditions: Array<Condition | ConditionGroup> // minimum 1 element
}

Groups can be nested to any depth, allowing expressions like (A AND B) OR (C AND D).

Single Condition

{
field: string, // field key (see table below)
operator: string, // one of the supported operators for this field type
value?: any // optional for null-check operators (isNull, isNotNull, isEmpty, isNotEmpty)
}

Filterable Fields

Field KeyLabelTypeSupported Operators
hostnameHostnamestringequals, notEquals, contains, notContains, startsWith, endsWith, matches, in, notIn, isNull, isNotNull
displayNameDisplay Namestringsame as hostname
statusStatusenumequals, notEquals, in, notIn
agentVersionAgent Versionstringsame as hostname
enrolledAtEnrolled Atdatetimeequals, notEquals, before, after, between, withinLast, notWithinLast, isNull, isNotNull
lastSeenAtLast Seen Atdatetimesame as enrolledAt
tagsTagsarrayhasAny, hasAll, isEmpty, isNotEmpty, contains

Operators Reference

OperatorApplies ToValue TypeDescription
equalsstring, number, boolean, date, enumscalarExact match
notEqualsstring, number, boolean, date, enumscalarNot equal
greaterThannumber, datescalarGreater than
greaterThanOrEqualsnumber, datescalarGreater than or equal
lessThannumber, datescalarLess than
lessThanOrEqualsnumber, datescalarLess than or equal
containsstring, arraystringCase-insensitive substring match (SQL ILIKE)
notContainsstringstringNegated case-insensitive substring match
startsWithstringstringCase-insensitive prefix match
endsWithstringstringCase-insensitive suffix match
matchesstringstringPostgreSQL regex match (~ operator)
instring, enumstring[]Value is in the provided array
notInstring, enumstring[]Value is not in the provided array
hasAnyarraystring[]Array field overlaps with provided values (&&)
hasAllarraystring[]Array field contains all provided values (@>)
isEmptyarrayArray field is empty
isNotEmptyarrayArray field is not empty
isNullanyField is NULL
isNotNullanyField is not NULL
beforedate, datetimedate stringDate is before value
afterdate, datetimedate stringDate is after value
betweennumber, date, datetime{ from, to }Value is between from and to (inclusive)
withinLastdate, datetime{ amount, unit }Date is within the last N units. Units: minutes, hours, days, weeks, months
notWithinLastdate, datetime{ amount, unit }Date is not within the last N units

Value Types

Depending on the operator, the value field accepts different shapes:

// Scalar string
{ "field": "hostname", "operator": "contains", "value": "srv" }
// Scalar number
{ "field": "hardware.cpuCores", "operator": "greaterThan", "value": 4 }
// Boolean
{ "field": "someBoolField", "operator": "equals", "value": true }
// Array of strings (for in/notIn/hasAny/hasAll)
{ "field": "status", "operator": "in", "value": ["online", "maintenance"] }
// Date range (for between)
{ "field": "enrolledAt", "operator": "between", "value": { "from": "2026-01-01", "to": "2026-02-01" } }
// Relative time (for withinLast/notWithinLast)
{ "field": "lastSeenAt", "operator": "withinLast", "value": { "amount": 7, "unit": "days" } }
// No value (for isNull/isNotNull/isEmpty/isNotEmpty)
{ "field": "displayName", "operator": "isNull" }

API Reference

All endpoints require authentication via JWT bearer token or API key. Routes are mounted under /api/v1.

Filters

MethodPathDescription
GET/filtersList saved filters for accessible organizations
POST/filtersCreate a new saved filter
GET/filters/:idGet a single saved filter by ID
PATCH/filters/:idUpdate a saved filter (name, description, conditions)
DELETE/filters/:idDelete a saved filter
POST/filters/previewPreview matching devices for ad-hoc conditions
POST/filters/:id/previewPreview matching devices for a saved filter

GET /filters

Query parameters:

ParameterTypeDescription
searchstring (optional)Filter the list by name or description substring match (case-insensitive, applied in-memory)

Response:

{
"data": [
{
"id": "uuid",
"orgId": "uuid",
"name": "My Filter",
"description": "Optional description",
"conditions": { "operator": "AND", "conditions": [...] },
"createdBy": "user-uuid",
"createdAt": "2026-02-18T12:00:00.000Z",
"updatedAt": "2026-02-18T12:00:00.000Z"
}
],
"total": 1
}

POST /filters

Request body:

FieldTypeRequiredConstraints
namestringYes1-200 characters
descriptionstringNoUp to 1,000 characters
conditionsConditionGroupYesMust contain at least one condition
orgIdstring (UUID)ConditionalRequired for partner and system scope; ignored for organization scope

Response: 201 Created with the saved filter object.

PATCH /filters/:id

Request body (all fields optional):

FieldTypeConstraints
namestring1-200 characters
descriptionstring | nullUp to 1,000 characters, or null to clear
conditionsConditionGroupMust contain at least one condition

Response: 200 OK with the updated saved filter object.

DELETE /filters/:id

Response: 200 OK with the deleted saved filter object.

POST /filters/preview

Request body:

FieldTypeRequiredConstraints
conditionsConditionGroupYesMust contain at least one condition
limitnumberNo1-100 (default 10)

Response: 200 OK with preview result (see Previewing Filter Results).

POST /filters/:id/preview

Query parameters:

ParameterTypeConstraints
limitnumber (optional)1-100

Response: 200 OK with preview result.

Search

MethodPathDescription
GET/searchSearch across devices, scripts, alerts, and settings

Query parameters:

ParameterTypeRequiredConstraints
qstringYes1-100 characters (trimmed)
limitnumberNo1-50 (default 20)

Response:

{
"results": [
{
"id": "string",
"type": "devices | scripts | alerts | settings",
"title": "string",
"description": "string (optional)",
"href": "string (settings only)"
}
]
}

Authorization Scopes

Both the filter and search endpoints require authentication. Filter endpoints additionally enforce scope-based access:

ScopeBehavior
organizationSees filters and devices within their own organization only. orgId is inferred from auth context.
partnerSees filters and devices across all organizations they manage. Must provide orgId when creating filters.
systemFull access to all filters and devices across the platform. Must provide orgId when creating filters.

Audit Events

All mutating filter operations generate audit log entries with the following action names:

ActionTrigger
filter.createA new saved filter is created
filter.updateA saved filter is updated (logged fields: changed field names)
filter.deleteA saved filter is deleted
filter.previewAn ad-hoc filter preview is executed
filter.saved.previewA saved filter preview is executed

Troubleshooting

Filter returns zero results unexpectedly

  • Check the field key. Field keys are case-sensitive. Use osType, not ostype or OsType. Refer to the Filterable Fields tables for exact keys.
  • Check the operator. Each field type only supports specific operators. For example, enum fields (like status) do not support contains — use equals or in instead.
  • Check the value type. The in and notIn operators require an array value, not a single string. The between operator requires an object with from and to keys.
  • Preview first. Use POST /filters/preview with your conditions before saving. This lets you iterate on the rule set without creating saved filters.

”Unknown field” error

The filter engine validates field keys against its known field definitions. If you receive this error:

  • Verify the field key matches one of the entries in the Filterable Fields tables.
  • For hardware, network, metrics, and software fields, ensure you include the prefix (e.g., hardware.cpuCores, not just cpuCores).
  • For custom fields, use the custom. prefix followed by the field key (e.g., custom.department).

Search returns no results for a known device

  • The search endpoint only matches against hostname and displayName for devices. It does not search by IP address, serial number, OS version, or other fields. For those, use the filter system.
  • The query string must be at least 1 character and at most 100 characters. Leading and trailing whitespace is trimmed automatically.
  • Search is case-insensitive, so casing is not the issue.

Partner user cannot create a filter

Partner-scoped users must include orgId in the request body when creating a filter. The API will return 400 Bad Request with "orgId is required for partner scope" if it is missing. The specified orgId must be an organization the partner has access to.

Preview limit behavior

The ad-hoc preview endpoint (POST /filters/preview) evaluates the filter across all organizations the caller has access to. Results are aggregated and then trimmed to the requested limit. The totalCount in the response reflects the total across all organizations before trimming. The maximum preview limit is 100 devices.