When you create an API key, you decide what data the integration can touch. Resource filters are how you express that decision. A filter says which entities the integration can see — families with a certain tag, details in a certain project, stashes a specific user created — and pairs with an action (read, write, or download) to form a complete scope grant.
This article walks through what filters are, when you actually need a custom one, and how to author one in the admin UI.
If you only need to grant "all families / all details / all stashes / all tags in a workspace," you can skip this article — Pirros ships system filters for those out of the box, and you'll see them as options when creating an API key. Read on if you need narrower access.
How filters fit into API access
A scope grant on an API key has three parts:
[workspace] + [filter] + [action]
The workspace scopes the grant to one workspace in your firm.
The filter says which entities of a specific type (Family / Detail / Stash / Tag) the integration can touch in that workspace.
The action says what they can do — read, write, or download.
A single API key can hold many of these grants — different workspaces, different filters, different actions. When the integration makes a request, Pirros checks whether any of the key's grants permits it (OR-semantics across grants).
When you need a custom filter
You want to grant... | Use |
Every family in a workspace | The system filter default:families |
Every detail in a workspace | The system filter default:details |
Every stash in a workspace | The system filter default:stashes |
Every tag in a workspace | The system filter default:tags |
Only families in one project | A custom filter |
Only details with a specific tag | A custom filter |
Only "typical" details, not project-specific ones | A custom filter |
Only stashes a specific user created | A custom filter |
System filters are pre-installed in every workspace and can't be edited or removed. Custom filters are the ones you author below.
Anatomy of a filter
Every filter has:
A name — admin-facing label (required, max 255 characters). Pick something descriptive (Concrete details (typical library), not Filter 3). An optional description (max 500 characters) is useful when other admins might wonder why you authored a particular filter.
A workspace — filters live inside one workspace and can't span workspaces. Both the workspace and the entity type are locked after creation (the edit form hides them); to change either, create a new filter.
An entity type — Family, Detail, Stash, or Tag.
One or more conditions — what the entity must satisfy. In the visual builder, conditions can be combined with AND and OR and grouped (nested up to 5 levels deep, max 10 conditions per group).
Note on NOT: the underlying predicate engine and the create/update API support a NOT (negation) node, but the visual builder does not expose a NOT control — the group operator offers only AND and OR, and rows have no negation toggle. If you need negation, you'd build it through the API, not the UI.
Creating a filter
Go to Settings → Resource Filters.
Click New filter. The builder asks for:
1. Workspace. Pick the workspace this filter lives in. (Can't be changed later.)
2. Entity type. Family, Detail, Stash, or Tag. The available attributes change based on what you pick — see the Attribute reference below. (Can't be changed later.)
3. Name and description. The name shows up wherever this filter is referenced — the API Keys creation flow, the filters list, audit logs.
4. Conditions. Build the predicate using the visual rows. Each row is [attribute] [operator] [value], where the builder shows the raw attribute and operator codes (e.g. project_type, eq, intersects) rather than prose labels. Add a row with the + button. Combine rows into groups using the AND / OR group control.
5. Preview the match count. Click Preview match count to run a count of how many entities in the workspace currently satisfy the filter. (The count is not live — it runs when you click, then refreshes as you edit.) If the filter matches more than 10,000, the count shows as 10,000+ (truncated) rather than an exact number. Preview is available for Family, Detail, and Stash filters; Tag filters can't be previewed.
Always check the match count before saving. Two failure modes to catch:
Zero matches — for a list/search grant, the integration's requests will return 403 insufficient_scope. Usually a sign that conditions are too strict or have a typo.
Way more than expected — usually a sign of a missing AND clause or an OR where you meant AND.
6. Save. The filter is now available in the API Keys creation flow as a scope option.
Attribute reference
What you can filter on, per entity type. The builder uses the raw field codes shown below.
Family (evaluated via the database — all attributes work)
Attribute code | Type | Example |
name | text | name eq "Beam Column Joint" |
category | text | category eq "Structural" |
classification | text | classification eq "Concrete" |
tags | list | tags intersects ["concrete","beam"] |
project_id | UUID | project_id in ["<project-uuid-1>","<project-uuid-2>"] |
created_at | date | created_at gt "2025-01-01" |
Detail
Attribute code | Type | Example |
type | text | type eq "concrete" |
project_type | text | project_type eq "typical" (vs. project-specific) |
tags | list | tags contains ["concrete","reviewed"] |
ℹ️ v1 limitation for details. Detail search / list / preview runs against the search index, which only supports type, project_type, and tags. Filtering details by status or created_at is not supported in v1 (planned for a later release). Filtering by a specific project_id works when reading a single detail by ID, but is not available in detail search/preview, so for a search-backed integration treat project_id as not-yet-available too.
Stash (evaluated via the database — all attributes work)
Attribute code | Type | Example |
project_id | UUID | project_id eq "<project-uuid>" |
is_permanent | true/false | is_permanent eq true |
created_at | date | created_at gt "2026-01-01" |
created_by | UUID | created_by eq "<user-uuid>" |
Values for project_id, created_by, and other UUID-typed attributes must be UUIDs, not names or emails. Date values must be ISO-8601.
Tag
Attribute code | Type | Example |
name | text | name eq "concrete" |
Operators
Operators available per value type (builder shows the raw codes):
Value type | Operators (code → meaning) |
Text | eq equals, neq not equals, in is one of, nin is none of |
Number | eq, neq, gt greater than, gte greater or equal, lt less than, lte less or equal, in is one of, nin is none of |
Date | eq, neq, gt, gte, lt, lte (no in/nin) |
List (tags) | intersects has any of, contains has all of, eq_set equals exactly |
True / False | eq |
UUID (project_id, created_by, …) | eq, neq, in, nin |
Notes: Date does not support in/nin ("is one of"). For Detail filters, the list operator eq_set ("equals exactly") is not supported by the search index and is rejected at evaluation — use intersects/contains for detail tag filters.
The builder only shows operators valid for the attribute you've picked.
Combining conditions
Rows in the builder can be grouped with AND and OR (the visual builder does not offer NOT — see the note under "Anatomy of a filter"):
AND (default) — an entity must satisfy all conditions in the group.
OR — an entity must satisfy at least one condition in the group.
You can nest groups up to 5 levels deep (max 10 conditions per group). For example, "concrete details OR steel details":
[ type eq "concrete" ] OR [ type eq "steel" ]
Attaching a filter to an API key
Once you save a filter, it shows up in the API Keys creation flow alongside the system filters. Go to Settings → API Keys, create or edit a key, find your filter in the scope grants section, and check the action(s) you want to grant (read / write / download — note tag filters offer only read / write, no download).
An API key can hold multiple filters of the same type — Pirros uses OR-semantics across grants. For example, a key with both default:families and "Concrete details (typical library)" can read every family and read concrete details from the typical library, but not other details.
⚠️ Editing a filter changes access immediately
Editing a filter's conditions immediately changes effective access for every API key that holds that filter — including in-flight tokens. There is no version, no grace period, no token-rotation gate: the API loads the current filter row on every request. Treat filter edits as a permissions change, not a refactor.
When you save an edit, the confirmation dialog tells you how many API keys reference this filter (e.g. "This will immediately affect 3 credentials. Continue?"). It shows the count only — not the individual key names. Read this number before clicking Save changes.
If you need to be conservative — for example, you're tightening a filter and want to verify integrations still work — do this instead:
Create a new filter with the new conditions.
Add it to the API keys that should use the new behavior (alongside the old filter).
Verify the integrations still work with the broader access (old + new).
Remove the old filter from those keys.
Archive the old filter when nothing references it.
This pattern lets you roll back instantly if something breaks.
Archiving a filter
To remove a filter, Archive it from the filters list. Archiving is a soft-delete:
The filter no longer evaluates — any API key still holding it loses that grant on its next request (list/write → 403 insufficient_scope; single-resource GET → 404 not_found).
The filter row is preserved so audit history still resolves to a real name.
Archived filters can't be un-archived from the UI. If you need it back, create a new one with the same conditions.
You can't archive or edit a system filter (both actions are disabled for them).
System filters
Pirros ships system filters per firm: four are created in each workspace, plus one firm-global filter:
Filter (name shown in UI) | Scope | Allows |
default:families | per workspace | Every family in the workspace |
default:details | per workspace | Every detail in the workspace |
default:stashes | per workspace | Every stash in the workspace |
default:tags | per workspace | Every tag in the workspace |
default:any | firm-global (one per firm) | Every entity type across the firm — appears once, labeled (firm-global) |
System filters appear with their literal default:* names, can't be edited or archived, and are pre-installed when the workspace (or firm) is created. Use them as the default for broad-access integrations.
Worked example: data warehouse with narrow access
Scenario. Acme Engineering uses a third-party data-warehouse vendor to pull detail metadata into a reporting dashboard. The vendor should only see concrete-tagged details in Acme's typical-details library — not project-specific details, and not steel or wood details.
Step 1. Author the filter.
Go to Settings → Resource Filters → New filter.
Workspace: Acme Main
Entity type: Detail
Name: Concrete details (typical library)
Description: Used by the data-warehouse vendor. Concrete details only, typical project type.
Conditions:
project_type eq "typical"
AND tags intersects ["concrete"]
Click Preview match count — it shows 1,247 details match. That tracks with what Acme expects. Save.
Step 2. Attach it to the vendor's API key.
Go to Settings → API Keys, either edit the vendor's existing key or create a new one named Acme Data Warehouse. In the scope grants section, find Concrete details (typical library), check Read, and save.
Step 3. Confirm.
When the vendor's integration calls GET /v1/workspaces/{acme-uuid}/details/{some-detail-id}:
If that detail is concrete + typical → 200 OK
If that detail is concrete + project-specific → 404 not_found
If that detail is steel + typical → 404 not_found
For a single-detail GET, a detail outside the granted filter returns 404 not_found (no existence disclosure) — not a 403. (403 insufficient_scope is what you'd see on the list/search endpoint, e.g. GET /details, when no eligible filter is granted.) Either way, the vendor sees exactly the slice Acme intended, and nothing else.
What's next
Set up a new API integration — Set up API access for your firm.
Reference for what developers see — Pirros External API: developer guide describes how filters appear in scope grants and 403 error bodies.
If you run into trouble authoring a filter or aren't sure how to express what you need, reach out to [email protected].


