With the release of Okta Privileged Access, an API has also been released to provide programmatic access into objects managed by it, such as servers, secrets and gateways. There is a set of Access Reports APIs to allow for external reporting on who has access to what and how. This article explores the APIs, the output and gives an example of using them in Okta Workflows.
The Access Reports API
The Access Reports API is a subset of the Okta Privileged Access (OPA) APIs. There are four endpoints:
- List all Access Reports – list all reports in the Team.
- Create an Access Report – create an access report for one user or one resource.
- Retrieve an Access Report – retrieve information about the access report, such as the id and status
- Download an Access Report – download the CSV contents of the report
The four endpoints all leverage a URL based on:
{opa_tenant}/v1/teams/{team_name}/access_reports
All endpoints require a bearerAuth token derived from a service account that has the security_admin role in OPA.
Generating an access report is an asynchronous process – you request creation (Create an Access Report) and are returned the Id of the new report. You may see a status of “CREATED“, “ERROR” or “IN PROGRESS” from the Create request. You may also get a status of “COMPLETED” or “COMPLETED_BUT_EMPTY” if the report was generated immediately.
You could use the Retrieve an Access Request endpoint to confirm if a report has completed (successfully, successfully but empty, or with error) before trying to Download it.
When you run the Download endpoint, you will get a CSV-formatted block of text, like the following, that you can process.

There are two types of Access Reports – user and resource.
- A user access report will show all accesses allowed for a specific user. To create a user access report, you pass it the OPA Id of the user and a resource_type_filter (currently only “server”). To get the Id for a user based on user name, you can use the Retrieve a User endpoint.
- A resource access report will show all accesses allowed for a specific resource. To create a resource access report you pass it the OPA Id of the resource and a resource_type (currently only “server”). There is no single endpoint to call to find a server based on name – you need to List all Servers and filter through the returned list to find the hostname or canonicalname, and get the Id for the matching server.
The List all Access Requests will produce a list with all reports, including their id, status and type.

The Report Output
The two reports, user and resource, produce similar results but in a slightly different order.
User Access Report
A sample output from a user access report is shown below.

This is showing all accesses a user is entitled to.
The User Name, Type and Status are straightforward, The Resource Name and Type show the resources this user has access to – in this case three different servers.
The combination of Account and Privileges shows the user access method for server access. i.e. what account/elevation they can access. For example:
- larry.linuxadmin + user – user can connect to the server using their individual account without any privilege escalation,
- larry.linuxadmin + admin – user can connect to the server using their individual account with escalation to admin (root equivalent on Linux, Local Administrator on Windows), and
- root + user – user can connect to the server as the root account (shared managed account in the vault).
The Conditions show how the user can leverage the access method:
- via an Okta Gateway – connection is routed via a gateway, but no session recording
- via an Okta Gateway with Session Recording – connection is routed via a gateway and the ssh/rdp session is recorded
- Approval – connection requires an approval from an access request first
- MFA – connection requires multi-factor authentication
To see what these accesses look like to the end user, have a look at https://iamse.blog/2023/12/01/leveraging-zero-standing-privileges-and-shared-account-access-with-okta-privileged-access/.
Resource Access Report
A sample output from a resource access report is shown below.

This shows all accesses against a specific resource.
The columns and values are the same, just that the resource is shown before the user.
An Example Using Okta Workflows
You can leverage the APIs from any programming language that supports calling REST APIs or something like Postman (there is an OPA Postman collection). But as I’m a huge fan of Okta Workflows, I implemented all four endpoints in flows.
Considerations for Workflows
There currently isn’t an Okta Privileged Access Connection in Okta Workflows. So you have two options:
- Call the API endpoints as Custom API commands in the Workflows Advanced Server Access connector. You need to establish the connection initially, but then you don’t need to worry about setting up the bearerAuth token for each call.
- Call the API endpoints as generic API Connector functions (e.g. Get, Post). You need to build the authorization header (with bearerAuth token) for the calls within workflow.
I chose to do the latter.
If you are using the generic API Connector, you should look at the Authentication section of the API Overview to see how to build the bearerAuth token.
I used a standard pattern for the main API Endpoint (building block) flows (one or two for each of the four endpoints):
- Pass in the relevant Id (user, resource or report) if needed
- Build the API Connector inputs. This includes the authorization object (bearerAuth token) for the request header and request URL. A common subflow was used to build the authorization object and URL prefix.
- Call the endpoint
- Process the body object
For example:

This made creating more flows simpler, as there were only minor variations in data in, the URL and how the output was processed.
The API Endpoint (Building Block) Flows
I built a basic set of flows around the four endpoints and the two types of reports.

The RPT1* flows are for the report Create endpoint, the RPT20 flow to Retrieve details of a report, the RPT3* flows are to Download a report, and the RPT40 flow is to List all reports.
Both RPT10 and RPT15 have the same structure. They will take a username or server hostname, convert that into the relevant id, then format the HTTP POST request body object to generate the report.

They then use the API Connector POST card to request creation of the report. If the request was successful there will be a 201 status code and the body of the response will contain details of the report.

The RPT20 flow will retrieve the details of a report based on the report id. The output is similar to above.

Both RPT30 and RPT35 have the same structure. They will take a report Id and download the report using the API Connector GET card. The body of the response, if successful (status code 200), will contain the CSV-formatted output (as shown above).
As there is no simple way to convert that output into a file object in Workflows, the output is converted into a list and each row processed individually (by RPT31 or RPT36) and each stored in a workflows table (the results of this are shown earlier in this article).
Workflows tables can be exported to CSV files for further analysis.
Stringing the Building Blocks Together for Business Flows
These flows could be building blocks to create reports of multiple users (e.g. all users in a group) or multiple resources (e.g. all servers in a project).
The following shows a flow built to generate an access report for all users in a group.

There are two flows, MAIN2 to get all the users in the group and MAIN2a to generate a report for each user and it to the workflows table.
The MAIN2 flow starts by setting a group name to process user from. Rather. than coding it into the flow, you could make this a delegated workflow to expose it in the Okta Admin Console and get the user to supply the OPA group name.
Then it runs a subflow to use some Groups API endpoints to find the group and list the members (by name).

It checks the returned user list to make sure it’s not empty.
Then it clears the output table (the one that the reports will be written to). It’s cleared before all the user reports are run, so that the table will contain ALL reports.
Then it processes each user in a subflow – MAIN2a.

For each user, MAIN2 will first run the RPT10 flow to create the user access report.
Then it will wait (30 seconds in this case).

This Wait For card is to allow OPA to complete the report which is checked with the RPT20 subflow. Ideally you would implement a “do until” loop mechanism where the RPT20 flow would call itself until one of the completed status’ is returned (or until you reach a limit or repetitions). In this case we’re just using the timer.
If the Retrieve subflow returns a COMPLETED status, the RPT30 subflow is called to download the report and store it in the table (adding to any entries already there).

The result is as shown below, the report output for each user in the group.

A similar approach could be used to find all servers in a resource group/project and build a report on them.
Utility Flows
As per my usual practice I built a set of utility flows for common functions, such as accessing my environment variables table and finding an Id for a username.
It is worth showing the utility flow (helper flow) used to construct the authorization object. For this flow:
- The PAM Okta URL and team name are retrieved by subflows (from storage mechanism of your choice)
- These are used to build the service_token endpoint

- The service account key Id and Secret are retrieved by subflows
- A request body object is constructed with them
- An API Connector POST is made to the service_token endpoint with the key id & secret object

- The bearer token value is extracted from the response body
- It is appended to the text “Bearer “
- This is converted into an object called Authorization and returned

You should ensure the key id and secret are stored securely. You should not “save all data that passes through the flow” for any flows that handle persistent security data, such as the service account key id and secret.
Conclusion
This article has explored the new Access Report API endpoints for Okta Privileged Access and how they can be used to generate user and resource access reports. It has looked at the report output and how to understand it to determine who has access to what and how. And it has provided an example implementation using Okta Workflows to produce reports and store them in a Workflows table.
