Product

Everything Assay does with your export.

One file in: your estate written down, connected, graded and signed. No credentials and no access to your Okta, at any point.

01

Export your folders

In the Okta Workflows console, open a folder and choose Actions → Export. Export as many folders as the estate spans; they are read together as one estate, so calls between folders resolve.

02

Assay reads it

The export is processed in memory. Your architecture is drawn in about a minute, and every flow is assessed, including the logic hidden inside cards.

03

You get the evidence

Your grade for free. A paid Assessment for every finding and a signed report. A Workspace to keep it current as the estate changes.

Okta removes connection credentials during export. Anything typed directly into a card stays in the file, and Assay checks for it.

Understand

See what the estate actually is.

Every flow as code

Each flow read back as plain logic an engineer can scan in seconds. Comments the builder wrote are surfaced, and calls into other flows are expanded where they run.

Hidden logic, found

A For Each body or an inline If Error handler is a complete flow the console shows as one card. Assay assesses each one: 560 listed, 867 assessed across 125 public template folders.

Flow map

Every card, branch and error path, with what each card takes and gives and which earlier card supplies it. Save it as PNG, JPEG or SVG.

Architecture

Five layers derived from the export, in the order things run, including flows coupled only through a shared table. Export it as an image or an animated GIF.

Dependencies

Which flow calls which, the entry points, and every flow that nothing in the export can start. Those are your retirement candidates.

Inventory

Every connection and table, and exactly which flows use each one, matched by id rather than by name.

Assess

Know how it holds up, and why.

51 controls, 7 dimensions

Error handling, logic robustness, security and access, intent and documentation, build standards, portability, and efficiency. The framework has 91; the 40 an export cannot prove are reported as not assessed, never passed.

A grade and a score

A risk grade from A to E, set by counting open findings rather than averaging, and a maintainability score from 0 to 100 inside that grade’s band.

Findings with evidence

Every finding names the flow and the card, and the fact in your file that produced it.

Improvement register

What to fix first, which flows each fix affects, and the effort involved.

Prove

Hand over evidence, not opinions.

Signed report

Signed with Ed25519 over the conclusions, not the PDF bytes. Anyone with the public key can verify it offline. PDF and HTML.

Documentation pack

A PDF describing each flow step by step in plain English, with its flow diagram. One flow or the whole estate.

Structured exports

The full assessment as JSON and the findings as CSV, for your own systems and trackers.

Keep it current · Workspace

Watch it change, and see what actually ran.

Version history

What each flow contained on a given date, card by card, and what changed between the version that worked and the one that didn’t. Renames are reported as renames.

Runtime evidence

Point Okta’s execution log stream at Assay and see whether a flagged call actually failed. Okta pushes to Assay; Assay never connects to Okta. Flow-level, as Okta sends it.

Change since last time

Each assessment compared with your baseline: flows added, removed and changed, and whether the grade moved.

Sign in with your Okta

Named sign-in through your own identity provider, so joiners and leavers are handled where you already handle them.

Who it answers

One file. A different answer for each person who asks.

CISO / Head of IAM

“What can our automation change, and how exposed are we?”

  • Every access-changing operation inventoried
  • One grade for the whole estate
  • Signed evidence for risk committees
IAM architect / platform lead

“What does this do, and what breaks if I change it?”

  • The architecture, derived from the export
  • Dependencies and flows nothing can start
  • Flows coupled through shared tables
Workflows engineer

“Where exactly is it fragile?”

  • Findings down to the card
  • The flow map, card by card
  • A prioritised improvement register
Auditor / GRC

“How do you know, and can you prove it?”

  • Every control with its evidence
  • “Not assessed” stated, never passed
  • A report verifiable offline, and change between assessments
MSP / delivery partner

“Is what we built any good?”

  • The same grade across every client
  • A professional handover document
  • Proof of quality, not a promise

The report

Click through the report.

Eight views from the report, generated from one real folder export. The live demo has every screen on a full sample estate.

Eight views from the report rail, as they appear in the tool
Report
Assessment conclusion
Every external call in this estate runs with no failure path
27 flows assessed · 18 declared · 9 found inside block bodies
30
Grade E
Critical

Thirteen of thirteen external calls accept the default finish policy, so a rate limit or a transient error leaves a change half-applied and reports success. One of those calls alters group membership. The grade is set by counting what is open, not by averaging: three unresolved Criticals is an E whatever the sections that scored well did.

What was found

Hidden flows
9
not listed in the console
Unguarded calls
13
of 13 external calls
Logging off
10
of 27 flows
Register items
4
in priority order
Signed report extract
Okta Workflows estate · one folder export
format 1.6.0 · 583,412 bytes · SHA-256 4f2b9e1c…8ad03e71
30
Grade E
Critical
Flows
27
18 declared · 9 hidden
Cards
237
across the estate
External calls
13
13 unguarded
Logging off
10
of 27 flows

Maintainability score, by dimension

Seven weighted dimensions. A dimension with no implemented control is reported as not assessed and its weight is redistributed, never scored as full marks.

DimensionScoreWeightBasis
Error handling & recoveryFailure paths, retries and recovery
18
20%
13 of 13 external calls run with no error policy
Logic robustnessRaces, unbounded reads and dead paths
61
17%
a governance write runs inside a loop set to ignore errors
Security & accessGuards on access-changing operations
34
17%
1 flow can alter access, guarded no better than the calls around it
Intent & documentationDescriptions, comments and stated purpose
52
12%
11 of 18 declared flows and 5 of 7 tables have an empty description
Build standards & namingNames the next engineer has to pay for
66
12%
9 of 27 flows still carry default card names
Efficiency & platform fitCall volume, iteration and native-card use
58
12%
external calls run inside two nested iteration stages
Portability & configurationValues tied to one tenant or environment
88
10%
no credential-shaped or hard-coded endpoint values found
Control results
Assay Okta Workflows Control Framework v1.2
91 controls in the framework, 51 of them answerable from an export · ten shown · a control that cannot be proven from an export is reported as not assessed, never as a pass
REL-001
External actions have failure handling
13 of 13 external-call cards accept the default finish policy.
Fail
REL-003
Errors are not silently suppressed
A governance write runs inside a loop set to ignore errors.
Fail
SEC-010
High-impact actions are auditable
The flow performing the access change has execution logging disabled.
Fail
GOV-010
Logging posture is deliberate
10 of 27 flows have execution logging disabled.
Fail
GOV-001
Every workflow has a description
11 of 18 declared flows and 5 of 7 tables have an empty description.
Fail
ARC-007
Unreferenced flows are reviewed
5 of 18 declared flows neither call nor are called within this export.
Fail
SEC-009
Access-changing operations are identified
1 access-changing card inventoried for review.
Pass
SEC-001
Secrets are not hard-coded
No credential-shaped values found in exported configuration.
Pass
GOV-002
Every workflow has an accountable owner
The export format carries no owner field.
Not assessed
GOV-009
Dormant flows are reviewed
The export carries no enablement state and no last-run timestamp.
Not assessed
Architecture
How the estate hangs together — derived, not drawn
5 layers · 18 objects · read out of the export, so it cannot drift

Nobody maintains this diagram. It is read out of the same export as everything else, laid out in the order things actually run, and each entry workflow’s chain is coloured end to end. Every link is routed in its own lane, so no line disappears behind a box.

What the diagram tells you that the console does not

Entry workflows
2
nothing in the export calls these
Called workflows
4
reached from another flow
Coupled by a table
2
pairs that never call each other
Reaches outside
1
endpoint with no native card

Coupled by a table is the one nobody documents. 2a. SAML Create App writes the App Registry table and 3. Group Assignment reads it. Neither calls the other, nothing in the console connects them, and changing the columns one writes breaks the other in production.

Methodology
Assay Okta Workflows Assessment Methodology v2.0
Published in full with every assessment, so a reader can check the reasoning rather than trust the number
Principles
Evidence first — compliance is never inferred from missing export data. Least privilege — access-changing operations are identified and reviewed. Resilience — external dependencies, errors, retries and recovery are assessed. Maintainability — complexity, decomposition and dependency depth are assessed. Operational readiness — ownership, monitoring and production controls are assessed. Data minimisation — sensitive values and external data transfer are identified.
Evidence levels
Direct — the exported workflow explicitly contains the evidence. Derived — the evidence follows from structure or relationships in the export. Insufficient — the export cannot reliably prove the control, and it is reported as not assessed.
Scoring
Two numbers, not one. A risk grade from A to E is set by counting open findings, so an estate with an open High finding cannot be graded A however much of it is clean. A maintainability score out of 100 is then placed inside that grade's band, so the letter and the number can never disagree. Each of the seven dimensions is scored from the severity-weighted findings raised against it, so a dimension carrying findings cannot score 100. A dimension with no implemented control is unassessed and its weight is redistributed across the rest.
The grade scale
Published in full, because a grade a reader cannot situate is a letter rather than a judgement. Read top down: the first rule that matches decides the grade.
GradeScoreBandWhat puts an estate here
A Clean89–100Excellentnothing open above Informational
B Minor issues73–88Goodan open Medium finding, or eight or more open Low
C Needs work56–72Needs attentionan open High finding, or eight or more open Medium
D At risk36–55High riskan open Critical finding, or five or more open High
E Unsafe to leave0–35Criticalthree or more open Critical findings
The grade counts what is open; it never averages. An estate carrying an unresolved Critical cannot be labelled Excellent because everything else passed. Every flow is graded the same way, so a flow can be worse than the estate it sits in.
Stated limits
The assessment reads only the export you supply. It does not connect to Okta, and it does not certify runtime behaviour. Where an export cannot distinguish between two explanations, the finding says so rather than choosing one.
Evidence fingerprint
format 1.6.0 · 583,412 bytes · SHA-256 4f2b9e1c…8ad03e71 — the signature covers the assessment, not the rendered document, so re-rendering does not invalidate it but changing a finding does.
The same operation, written out — including the flow it calls
flow add_users_from_a_table_to_an_okta_group():
    rows = stash.searchRows2(sort_direction="asc", sort_limit=200, stash=table[…], …)
    for each item in rows, …:
        # ─── enter Get user Okta id and add to group ───
            raw_output = okta.searchUsers(resultset="First Matching Record", query=email)
            # unguarded — flow stops here on failure
            if id != "":
                compose = compose(text="/api/v1/groups?search=profile.name eq "{{…", 3hugqyie0=groupname)
                status_code = okta.hTTPRequest(requesttype="GET", request_relative_url=compose)
                # unguarded — flow stops here on failure
                get = get(object=body, path="0.id")
                status2 = okta.addNewUserToGroup(group_id=get, user_id=id)
                # unguarded — flow stops here on failure
                join()
            else:
                return
        # ─── back ───
Drag any card · click for what it evaluates
SoundWorth reviewing No error handling Called flow Block body
Improvement register
Four items, in priority order
Effort is our estimate for a team already familiar with this estate
R1Attach a failure path to every external callHigh prioritySmall effort
What to change

13 of 13 external calls stop the flow on failure. Workflows supports a single shared handler flow referenced from each card.

What changes as a result

A rate limit or a transient error retries, or records itself, instead of leaving a change half-applied.

R2Stop discarding failures inside iterationHigh priorityMedium effort
What to change

A governance write runs inside a loop set to ignore errors, so per-item failures disappear.

What changes as a result

A partially applied change is reported as partial rather than as success.

R3Enable execution history where access changesMedium prioritySmall effort
What to change

10 of 27 flows have logging switched off, including flows on the grant path.

What changes as a result

Every access change leaves a record an auditor can retrieve.

R4Document the tables, remove the dead weightMedium prioritySmall effort
What to change

5 of 7 tables carry no stated purpose, and 5 flows are referenced by nothing.

What changes as a result

A new engineer can tell what each table holds and which flows still matter.

Assay reports and does not remediate. These items are written so your team, your integrator or a third party can act on them — and so the next signed report can measure whether they were.

What we will not claim

The limits, stated up front.

Whether a flow is switched on. An export carries no enabled or disabled marker, so Assay reports what can reach a flow, not whether it runs.
Who owns a flow. The export has no owner field. Ownership is reported as not evidenced, never guessed.
Which card ran. Okta’s execution stream is flow-level. Assay names the flow, the run and the outcome, and the failing card only where Okta sends it.

See it on your own estate.

Draw your architecture free in about a minute, or explore every screen in the live demo.