Finance & SaaS SpendSaaS Discovery
On this page0

Unfamiliar word? See Glossary: words used in these docs. Looking for the tasks in order? Begin at Start Here.

Reference

Included in: Team and Advanced. Which Plan Includes What

SaaS Discovery

SaaS Discovery finds the apps your people actually use — by reading usage signals your connected identity providers already emit. The signals are read after each directory sync and added up per app on a read-only screen. It answers a different question from SaaS Spend: Discovery is who signs in to what, Spend is what you pay.

Usage, Not Invoices

Each discovered app is scored against your directory: how many distinct users touched it, how many are still active in a rolling ~30-day window, how many matched accounts have gone dormant, and whether any account in it belongs to nobody in your directory — shown on the screen as Unmatched accounts.

App: Figma  ·  Sources: Okta + Google Workspace → CORROBORATED

Users: 41  ·  Active (30d): 33  ·  Dormant: 8

App: Grammarly  ·  An account matches nobody in the directory → UNMATCHED ACCOUNTS (status: unmanaged)

→ 33 active users pushed to Finance for reclaimable-spend math

What Discovery Shows

Every figure below is derived from sign-in and audit events your identity providers already record — Discovery never logs in to the app itself to read its usage, and it never writes anything back to your directory. An account is called dormant when a matched user is assigned to an app in Okta but has no SSO sign-in to it in the last 30 days. Dormant counts come from Okta only — Google Workspace reports activity, not assignments, so it never produces a dormant row. Dormant is a candidate to review or reclaim, not proof a licence is idle.

Per-App Rollup

One row per discovered app: distinct users, active users (signed in within the rolling ~30-day window), dormant accounts, category, the sources it was seen from, last-active time, and a per-user drilldown.

Unmatched Accounts

An app is marked Unmatched accounts when at least one account seen in it is not a user in your directory — a contractor, a personal address or someone who has left. Every other app reads All users matched. Apps with unmatched accounts sort to the top of the list. It is a directory match, not an approval: nobody approves or rejects an app on this screen.

Corroboration Across Sources

An app seen from two or more sources — Okta and Google Workspace, say — is marked corroborated, so a single noisy signal is easy to tell apart from usage two providers agree on.

A Bridge To Reclaimable Spend

Per-app active-user counts feed the Finance service so the SaaS Spend page can put paid seats next to actually-active users — turning “reclaimable spend” into a real number instead of a guess. Dollars stay owned by Finance.

Discovery Rides AlignSync (Team and up)

SaaS Discovery is part of the AlignSync entitlement, which starts on Team ($649) — so it is included in Team and Advanced, and is not in Core ($299). The reclaimable-spend figure it feeds needs the Finance module, which is Advanced ($1,799). There is no separate licence switch for Discovery: the AlignSync entitlement that governs directory reconciliation governs this too. All Zonitix pricing is per instance, never per seat.

How Unmatched Accounts Surface

Each account is matched to your directory, first by the provider’s own user id and then by email address (ignoring capital letters). An account that matches nobody is kept with its email address and labelled unmatched, and one such account is enough for the app to read Unmatched accounts (status: "unmanaged" in the API). It tells you an account is not one of your directory users. It does not tell you whether anyone approved the app.

An unmatched account is counted under Users but never as active or dormant, and it is left out of the active-user counts sent to Finance. An app where no matched person is active is not sent to Finance at all: it stays on the Discovery page as something to look into.

The Discovery → Finance Bridge

After a discovery run, per-app active-user counts for matched users are pushed to the Finance service. That is what lets the SaaS Spend page compute real reclaimable spend — paid seats measured against actually-active users, and cost per active user — instead of guessing at utilization. The push is best-effort: dollars remain owned by Finance, and Discovery’s counts are only merged into spend when the finance entitlement is licensed. An app with no active matched user is not in that push; it stays on the Discovery page.

Discovery Owns Usage

Who signs in, how recently, from which providers, and whether the account maps to a real directory user. This is the side that decides “active” and “dormant”.

Finance Owns Dollars

Cost per app comes from committed invoices on the Finance side. Reclaimable spend is only the merge of the two — active users from Discovery, cost from Finance — and only when the finance entitlement is on.

Honest Boundaries

Source-Bounded — Not A Scanner

Discovery is built from the IdP usage signals you have connected — Okta app assignments and System Log, and Google Workspace Reports. From Okta it reports only apps configured in Okta and the people assigned to them; from Google, apps that appear in its OAuth-grant and SAML audit logs. It is not a network scanner and not an endpoint agent: an app that never touches a connected IdP will not appear via this path. Read this as “the apps your identity providers can see,” not “every app in use.”

Read-Only w.r.t. Identity

Discovery never creates or updates directory users, and never moves money. It reads usage and reports it — nothing about it writes back to your directory or your ledger.

Fail-Safe By Design

If a read fails, the page shows an empty result instead of an error. A failure in discovery cannot undo a directory sync that already succeeded, and a failure to pass user counts on to Finance does not stop discovery.

Distinct From SaaS Spend

The SaaS Spend tab in Finance is invoice- and cost-based — what you pay. SaaS Discovery is usage-based — who signs in to what. They are separate views that connect one way: Discovery feeds active-user counts into Spend so reclaimable spend is grounded in real usage. See SaaS Spend Tracking for the cost side.

If something here is not working as you expect, start with When something goes wrong.

The plain explanation is above. This part is for IT and developers: the exact settings, permission names, API addresses and data fields behind it. Closed: select the heading above to open it. While it is closed, your browser’s find-on-page (Ctrl+F or Cmd+F) does not search this part.

The screen is at /dashboard/saas-discovery.

The view_saas_discovery Permission

Reading Discovery is a separate, read-only right: view_saas_discovery. It exists for least privilege — a finance or IT-cost owner can see usage and reclaimable spend without holding AlignSync’s directory-write powers. The permission implies view_dashboard so its holder can reach the dashboard shell; it grants nothing beyond reading the discovery views.

The Discovery Endpoints

Three read endpoints back the console, all served through your instance gateway under /user-management/discovery/*. Every one requires view_saas_discovery and carries a soft alignSyncEnabled gate — integration-service is a core container, so it is the entitlement that gates the feature, not the container: a direct call is refused (403) only when alignSyncEnabled is explicitly turned off.

MethodPathPurposeAuth
GET/user-management/discovery/summaryKPI tiles for the console header
returns appsDiscovered, shadowItApps (apps with at least one unmatched account), dormantAccounts, activeUsers, sources
view_saas_discovery
GET/user-management/discovery/appsOne record per discovered app, wrapped as { apps: [...] }
returns appKey, appName, category, sources, corroborated, userCount, activeUsers, dormantAccounts, status ("approved" = every account matched | "unmanaged" = at least one unmatched; the field names are older than the screen labels), lastActiveAt
view_saas_discovery
GET/user-management/discovery/apps/{appKey}/usersPer-person drilldown for one app, wrapped as { appKey, users: [...] }, most recently active first
returns userId, userEmail, source, status, lastActiveAt
view_saas_discovery

// KPI tiles for the Discovery console.

curl 'https://<your-instance>-api.zonitix.com/api/v1/user-management/discovery/summary' \

-H 'Authorization: Bearer <token>'

// Response:

{ "appsDiscovered": 12, "shadowItApps": 3, "dormantAccounts": 27,

"activeUsers": 208, "sources": ["OKTA", "GOOGLE_WORKSPACE"] }

// App records — apps with unmatched accounts ("unmanaged") sort to the top; appKey is what the drilldown URL takes:

GET /api/v1/user-management/discovery/apps

{ "apps": [ { "appKey": "grammarly", "appName": "Grammarly", "status": "unmanaged",

"corroborated": false, "userCount": 4, "activeUsers": 0, "dormantAccounts": 0,

"sources": ["GOOGLE_WORKSPACE"] } ] }

Where The Data Comes From

Usage is read after a directory sync from the identity providers you have connected, over a rolling 30-day window.

  • Okta contributes the apps configured in Okta and the people assigned to them, joined to System Log SSO sign-in events. An assignment with a sign-in in the window is active; one without is dormant. Enumeration is capped at the first 200 active Okta apps so a huge tenant cannot stall the sync.
  • Google Workspace contributes two audit streams from the Reports API: third-party OAuth grants (token) and SAML logins (saml). Every Google row is active usage.
  • Google needs one extra scope. Directory sync does not request it. In Google Admin, under domain-wide delegation, add https://www.googleapis.com/auth/admin.reports.audit.readonly to the service account’s client ID. Without it the Reports read fails quietly: Google contributes nothing to Discovery and the page shows no error.
  • Snapshots. On a clean run, each provider’s usage rows are replaced in one transaction. If a read is partial (a rate limit or an API failure) and a previous snapshot exists, the previous snapshot is kept rather than overwritten with incomplete data, so the figures can be older than the last sync. A run that overlaps one already in progress is skipped.
  • Paused and deleted hooks. Only usage from hooks that are switched on (isActive) is counted, in all three endpoints. Pausing a hook hides its apps and people from Discovery without deleting the rows, so switching it back on shows them again. Deleting a hook deletes its usage rows.

Related reference