Malleeon

Okta Workflows Has No Audit API, So I Reverse-Engineered One

No public API exists to list flows and folders in Okta Workflows, so I reverse-engineered the Console's own internal endpoints to build one.

5 min read
okta workflows api security


A routine compliance audit needed a simple thing: a list of every active flow in our Okta Workflows org, with the folder each one lives in. Flow name, folder path, on or off. That’s it.

With 158 active flows spread across roughly 74 folders, several nested two or three levels deep, doing this by hand wasn’t realistic. This should have been a five-minute API call.

Everything below happened inside a browser session I already had open and already authenticated. I wasn’t extracting credentials or bypassing auth, just watching the requests my own admin session was already making and replaying them from the browser console. Worth saying that up front, because the rest of this post is about undocumented endpoints, and I don’t want that framing lost.

There’s no API for this

Okta Workflows has a proper public API for running flows: endpoint triggers, webhooks, OAuth2 invocation. There’s no API for managing or listing flows and folders. You can’t ask Okta “give me every flow in my org and which folder it’s in.”

I confirmed this wasn’t a gap in my own research. On Okta’s developer forum, an Okta staff member was asked this exact question in November 2025, someone doing a security assessment wanted to list flows and their connectors, and the answer was that no public API exists, with a pointer to an open Idea (Workflows Config / Exports API) as the only thing on the roadmap.

That Idea has been open since 2021. Back then, Okta support said a public API was targeted for the first half of 2022. It’s mid-2026 now. Four and a half years later, nothing has changed.

The only officially supported route for bulk extraction is the folder Export feature in the Console, one folder at a time, subject to org-wide export rate limits that reset every 15 minutes, and capped by subscription tier. It gives you a .folder/.flopack file you then have to parse yourself. For 74 folders, that’s a lot of clicking to get what should be a single list.

What actually worked

I ruled out the official export quickly. Too manual, too rate-limited, not built for auditing an org at this scale.

What I tried instead: the Workflows Console is a single-page app, so it has to call some backend to render the folder tree and flow lists on screen. Those calls aren’t documented or supported, but they’re ordinary authenticated requests my own browser was already making. I could inspect them in DevTools and replay them from the console.

Getting there took real trial and error. I captured requests in the Network tab and worked through a dozen or so candidate endpoint names: flo, group, org, all, configuration, flostats, ancestors, count. Several looked promising from the name alone and turned out to be permission metadata or unrelated config. Three endpoints actually mattered: org for the root folder list, group for a folder’s children, and flo for the flows inside a folder. None of them documented anywhere.

Then I hit a silent depth limit. Asking for a folder’s children using just its own ID worked fine for folders sitting directly under a root folder. For anything nested two or more levels deeper, it returned an empty array. No error, no warning, just nothing. I only found this by comparing a folder I could see had children in the UI against an API response claiming it had none. The fix was passing the full dotted ancestor chain (rootId.parentId.thisId) rather than the folder’s own ID.

That silent failure meant my first working script undercounted. It found 69 of roughly 74 folders and came up 14 active flows short of the Console’s own reported total of 158. Nothing in the response flagged an error, the folders just looked empty. I caught it by cross-checking the discovered folder count against a rough manual count of the sidebar tree, then instrumenting the script to flag any folder that claimed hasSubfolders: true but returned nothing.

Once I fixed the path construction, the script’s active flow count matched the Console’s own dashboard exactly. 158 of 158.

The end result is a browser-console script that walks the full folder tree, pulls every flow from every folder, and exports a CSV: flow name, folder path, active status, type, trigger, last-run date. The whole audit in one pass, tied out exactly against Okta’s own numbers.

Why this shouldn’t have taken as much time as it did

None of this is exotic. I wasn’t doing anything Okta doesn’t already do internally, the Console makes these exact calls every time an admin opens the Flows page. I just had to reverse-engineer, by hand, an API that Okta’s own frontend team already built, tested, and ships to production every single day. That’s not a research project. That’s supposed to be a ten-minute job with a documented endpoint, and instead it took hours of poking at undocumented ones to get right.

It’s also not a one-off need. Security reviews, compliance audits, onboarding a new automation engineer, or just periodically asking “what have we actually got running in here?” all need this same list. Any org running Workflows at scale will eventually want it. This is the most basic inventory question you can ask of an automation platform, and four and a half years after the idea was first raised, Okta still hasn’t got round to answering it.

Relying on undocumented internals to work around that is a bad place for any customer to be, for something this basic. Okta could change these endpoints’ shapes, add new depth quirks, or block the calls outright, with no notice and no changelog entry, because from their side this was never a public contract. It’s implementation detail I wasn’t supposed to be looking at. That’s a genuinely fragile foundation for something as unglamorous as an inventory list, and it’s a foundation I only needed because the supported alternative doesn’t exist.

The silent-failure behaviour on deep folders is the part that sticks with me most. An empty array instead of an error is exactly the kind of edge case a properly versioned, publicly documented API gets caught for in testing, because external customers are meant to be relying on it. Internal tooling doesn’t get that scrutiny, because nobody outside the building is meant to be looking at it. Which was precisely my problem: I was looking at it, because there was no other way to get an answer to a question this basic.

If Okta ever does build this out, the bar is low: list folders with parent/child relationships and no silent depth limits, list flows per folder with status, type, trigger, and last run, and ideally surface which connectors and identities each flow authenticates as. That last one is the same thing another admin was asking for on the forum, for the same reason I needed it.

I’ve used Workflows since it landed in the Okta ecosystem, and none of this surprises me anymore, it’s a pattern. Connectors break in ways that don’t surface until a flow silently fails in production. Debugging inside the flow builder is still largely guesswork, half the time the only way to find out why something didn’t fire is to bolt on manual logging steps and hope. Long-standing gaps get raised on the community forum, an Okta employee drops in to say it’s “on the roadmap,” and then nothing happens for years. Meanwhile the SKU list keeps growing and the price keeps climbing. Funny how the monetisation roadmap never seems to slip.

The idea’s been sitting on Okta’s own ideas portal since 2021, quietly outliving a few pricing changes along the way. If you’ve hit the same wall, it’s worth an upvote. I wouldn’t hold my breath waiting on it though.

← All posts