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 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 queries.
All fields are optional. Multiple sibling fields are combined with AND.
Name match
stringMatchType is one of EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH.
Description match
string (case-insensitive substring match). User entities have no description and never match this filter in either polarity. Set not: true to return resources and groups whose description does not contain string (including entities with an empty description).
Tag match
key is required. value is optional — if omitted, matches only tags with no value (use entityTagKey to match any value for a key). connectionId is optional — set it to restrict to tags sourced from a specific connection.
Tag key match
keys, regardless of the tag’s value (including tags with no value).
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 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 queries
An ACCESS query returns paginated access grants — each result represents one(principal, entitlement, access level) grant, along with whether it is direct, its effective expiration, and all intermediate paths connecting the principal to the entitlement.
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
isDirect—trueif any path inpathsis a direct 2-node grant ([principalId, entitlementId]).effectiveExpiration— when this grant expires across all paths;nullif at least one path is permanent.paths— all paths through whichprincipalIdholds this access level onentitlementId. Each entry includes thepatharray of entity IDs from principal to entitlement and that path’sexpiration(nullif permanent).
Examples
All access grants 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.