
“Show me every account that’s either overdue or flagged high-risk, opened in the last quarter, in one of five specific regions.” That’s an ordinary question for whoever owns a book of accounts, and until recently it had no direct answer in the product. A filter could match one field against one value at a time — every condition ANDed together, nothing optional, nothing grouped. Answering the real question meant exporting to a spreadsheet, filing a report request, or writing a one-off script. None of those give you a saved, reusable, live view.
Entity queries now support the shape that question actually has: conditions grouped with OR, membership and range checks in a single clause, and filtering one record by a field on something it’s linked to — all without leaving the product, and all through the same filter grammar that already powers search, CSV export, dashboard rollups, and every AI agent built on the API.
What this unlocks for the person asking the question
- “Any of these, none of those.” Filtering by list-of-values membership — a set of regions, a set of statuses — is one clause instead of a stack of ORed single-value filters bolted together by hand. In the UI, this now shows up directly on any List-type field as multi-select “Is Any Of” / “Is None Of” chips.
- “Either/or” conditions. A view can now say “overdue OR high-risk” instead of only “overdue AND high-risk” — the two failure modes analysts run into constantly when a tool only ever ANDs.
- Ranges without two separate filters. “Opened between January and March” is one clause, not a “greater than” filter paired with a “less than” filter that both have to be kept in sync.
- Filter by what a record is linked to, not just its own fields. “Every order where the customer’s tier is Enterprise” filters the Order records directly by an attribute on the Customer they reference — no separate lookup, no client-side join.
- One grammar, every surface. The same filter definition works whether it’s driving a saved workspace view, a CSV export, a dashboard’s rollup, or a query an AI agent writes on the fly through the API — write the logic once, reuse it everywhere it applies.
The filter grammar
A query is a list of groups; clauses inside one group are ANDed, and the groups themselves are ORed together. An empty list applies no filter at all:
[
[
{ "field": "status", "operator": "in", "value": ["overdue_id", "high_risk_id"] },
{ "field": "created_at", "operator": "between", "value": ["2026-01-01", "2026-03-31"] },
{ "field": "customer.tier", "operator": "eq", "value": "enterprise_id" }
],
[
{ "field": "priority", "operator": "eq", "value": "urgent_id" }
]
]
Read as: (status in [overdue, high_risk] AND created_at between [Q1] AND customer.tier = enterprise) OR (priority = urgent).
The operator set:
| Operator | Value shape | Meaning |
|---|---|---|
eq / neq | single value | Equals / not equals |
gt / lt | single value | Greater than / less than |
like / not-like | single value | Case-insensitive substring match / mismatch |
in / not-in | list of values | Is one of / is none of |
between | [lower, upper] | Inclusive range — numbers, dates, or timestamps |
empty / not-empty | none | Field has no value / has a value |
List and reference attributes always compare against the stored identifier, never the display label — the same identifiers already returned in a record’s list_item_ids and reference_entity_ids.
Filtering through a reference, in one hop
The field in a clause can also be <reference attribute>.<attribute on the target template> — one hop through a link, resolved server-side:
curl -X POST "https://api.omnismith.io/v1/entities/search/order" \
-H "Authorization: Bearer omni_live_secret_key_..." \
-H "X-Omnismith-Project-Id: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{
"filter_groups": [
[{ "field": "customer.tier", "operator": "eq", "value": "018b2f1b-…-enterprise" }]
]
}'
The caller’s own permissions apply on both ends of the hop: filtering through a reference into a template the caller may not view at all comes back as a 403, and any row-level access scope already in place on the target template applies to the hop automatically — a filter can never surface a related record through the back door that a direct query on that template would have hidden. An unknown field, an operator that doesn’t fit the attribute’s type, or a malformed value is refused with a 400 naming exactly which clause is wrong.
What’s in the visual filter builder today
The workspace’s filter UI now exposes Is Any Of / Is None Of as multi-select options on any List attribute — pick several statuses or categories from one dropdown instead of adding a filter chip per value. OR groups, range filtering, and reference-hop filtering are available today through the API and every MCP tool built on search_entities, for a saved report, an integration, or an agent composing a query on the fly; bringing OR groups and cross-reference filtering into the same visual builder is the natural next step for this feature.
One grammar, four consumers
filter_groups isn’t scoped to search alone — the same clause and group shape drives search_entities, CSV export, dashboard rollup blocks (stat tiles, gauges, lists, charts), and the aggregate group-by endpoint. A filter written once for a saved view answers the same question consistently everywhere that view’s logic needs to show up, instead of being re-expressed by hand for the export button and the dashboard tile separately.
None of this changes what a record looks like — it changes how precisely you can ask for the ones that matter. The same identifiers a record already exposes (list_item_ids, reference_entity_ids) are what a filter clause compares against, so a query written against the schema today keeps working exactly as the schema grows.