POST /queries/run is in beta and limited to organizations in the OpalQuery beta. Contact Opal support to be added.POST /queries/run endpoint is the programmatic equivalent of the OpalQuery builder — useful for scripted audits, reporting pipelines, and one-off investigations.
Requirements
See Requirements in the Overview. You’ll also need an Opal API token — see Authentication.Endpoint
Query types
Every request setstype to one of two modes:
Both modes use the same filter vocabulary —
AccessEntityFilters to describe entities, and AccessRelationshipFilters to traverse access edges. The Entity filters and Relationship filters sections below document these shared building blocks; the NODE queries and ACCESS_PATH queries sections show how each mode uses them.
Entity filters
AnAccessEntityFilters object describes a set of entities by their own properties. It is used in both modes: as nodeFilters / accessFilters.isAccessibleBy / accessFilters.hasAccessTo in NODE queries, and as principalFilter / entitlementFilter / the nested filters inside principalAccessFilters and entitlementAccessFilters in ACCESS_PATH queries.
All fields are optional. Multiple sibling fields are combined with AND.
Name match
stringMatchType is one of EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH.
Tag match
key is required. value is optional — omit to match any value for that key. connectionId is optional — set it to restrict to tags sourced from a specific connection.
IDP status match
statuses is one or more of ACTIVE, SUSPENDED, DEPROVISIONED, DELETED, NOT_FOUND, combined with OR. Groups and resources never match this filter, in either polarity. Set not: true to invert within the user domain (e.g. “users whose IDP status is NOT active”).
Admin owner match
ownerIDs is a list of owner (group) UUIDs; an entity matches if its admin owner is any of them. User entities never match this filter, in either polarity. Set not: true to return resources/groups not owned by the given owners.
Logical composition
allOf, anyOf, and not each take the same shape as the outer filter and can be nested arbitrarily deep.
env=prod and team=platform, excluding AWS IAM roles.
Relationship filters
AnAccessRelationshipFilters object traverses access edges. It is used in both modes: as accessFilters in NODE queries to narrow returned entities by their access relationships, and as principalAccessFilters / entitlementAccessFilters in ACCESS_PATH queries to scope one side of the path by its own access edges.
Both fields take an
AccessEntityFilters object. Both direct and indirect (through nested group memberships) edges are considered. When both are set, both must be satisfied.
hasAccessTo additionally accepts two fields that constrain the role on the access edge:
These are only honored inside
hasAccessTo — they have no effect in isAccessibleBy or entity filters.
NODE queries
A NODE query returns a paginated list of entities (users, resources, or groups) that match your filters.Request
At least one of
nodeFilters or accessFilters is required.
Response
node.id is the entity’s Opal UUID — pass it to other API endpoints (e.g. GET /resources/{resource_id}) to fetch full details.
Examples
All usersenv=prod, accessible by contractors
Combines entityItemTypes, entityTag, and accessFilters.
write access to a specific GitHub repo
Uses roleRemoteIds inside hasAccessTo to filter by role.
entityAdminOwner and hrIdpStatus.
ACCESS_PATH queries
An ACCESS_PATH query returns paginated access paths — each result is one principal → entitlement pair, the terminal access level, the depth, and the chain of entity IDs in between.Request
At least one of
principalFilter or entitlementFilter is required.
Edge filter
directOnly— whentrue, return only direct (depth-1) principal → entitlement edges.accessDurationType—EXPIRING_ONLYorPERMANENT_ONLY, to constrain by whether the terminal access expires.
Advanced access filters
principalAccessFilters and entitlementAccessFilters scope one side of the path by its own access edges, using the same AccessRelationshipFilters shape as NODE accessFilters:
principalAccessFilters.hasAccessTo— keep only principals that also have access to a matching entity.principalAccessFilters.isAccessibleBy— keep only principals that are also accessible by a matching entity.entitlementAccessFilters— the same, applied to the entitlement side.
Response
depth— number of hops from principal to entitlement (1= direct).path— entity IDs along the chain from principal to entitlement.totalCount— exact count of matching paths; only populated whenincludeCount: truewas set on the request.
Examples
All access paths for a specific userbilling-prod and also hold admin access
Uses principalAccessFilters.hasAccessTo to scope principals to those with an additional access relationship.
hrIdpStatus on the principal side with directOnly on the edge.
Pagination
Both query types use cursor-based pagination. Setfirst to control the page size (default 200). When pageInfo.hasNextPage is true, pass pageInfo.endCursor as after on the next request.
includeCount on pages after the first — the total count is stable and recomputing it on every page is expensive.