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
Unmatched Accounts
Corroboration Across Sources
A Bridge To Reclaimable Spend
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
Finance Owns Dollars
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
Fail-Safe By Design
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.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /user-management/discovery/summary | KPI 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/apps | One 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}/users | Per-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.readonlyto 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.