How to Pull User Lists from Any SaaS App with a Unified Directory API
Stop hardcoding point-to-point API connectors. Learn how to extract users, roles, and access levels across 100+ SaaS apps using a single unified directory schema.
If your roadmap includes “pull a list of users, roles, and access levels from every app our customer uses”, you don’t need 50 bespoke connectors. You need a Unified User Directory API.
You are sitting in a sprint planning meeting. Your product manager drops a seemingly simple requirement: "Our customers need to pull a list of users from all the SaaS apps they use so we can run access reviews."
You do the math. According to BetterCloud's State of SaaS report, the average company uses 106 different SaaS applications. Grip Security data shows that up to 90% of SaaS applications and AI tools operate entirely outside of centralized IT oversight.
Your engineering team can realistically build, test, and maintain maybe two production-grade API integrations per quarter. Building point-to-point connectors for 100+ platforms means this feature will be completed sometime in 2035. You are staring down the barrel of terrible vendor API documentation, aggressive rate limits, and undocumented edge cases.
Identity sprawl is breaking product roadmaps. Customers expect total visibility into who has access to what, but the sheer volume of integrations required makes it an engineering nightmare.
Why Standard SCIM and IdP Integrations Aren't Enough
The standard engineering reflex is to integrate with Okta, Microsoft Entra ID, or Google Workspace and call it a day. If a customer wants user data, they should just push it via SCIM.
This approach fails upon contact with reality. Connecting to major Identity Providers (IdPs) is table stakes, but it leaves a massive blind spot.
If you’ve built SCIM before, you already know the two main failure modes:
- Coverage is bad. Lots of apps don’t support SCIM at all, or only support it on enterprise plans. These tools live in the shadows of enterprise identity.
- Even when SCIM exists, implementations differ. Okta uses HTTP PATCH for user activation, while Entra ID doesn't support PUT at all.
More importantly, SCIM is a push-based protocol. It tells your app about changes in the IdP. It doesn't let you pull a snapshot of who has access to what, across every tool, right now.
If your platform only pulls users from the top five IdPs, you are missing the vast majority of active accounts and ignoring localized identity drift. To get an accurate, auditable list of users, you have to query the end applications directly. You need a programmatic way to extract users from the HRIS, the CRM, the ticketing system, and the niche industry tools.
Introducing the Truto Unified User Directory API
We built the Truto Unified User Directory API to solve this exact problem. Instead of learning the idiosyncrasies of 50 different APIs—different field names, weird pagination styles, bespoke authentication methods—you call a single endpoint.
A well-designed Unified User Directory API normalizes the full identity and access graph into a consistent data model across clear, logical domains:
| Domain | Entities | What You Get |
|---|---|---|
| Identity & Access | Users, Roles, RoleAssignments | Who exists, what permissions they have, how those map |
| Organizational Structure | Organizations, Workspaces, Groups | Tenant hierarchy, teams, mailing lists |
| Licensing & Billing | Licenses, Utilization | What seats are provisioned, what's actually being used |
| Auditing | Activities | Login events, admin changes, policy violations |
When you make a request to list users, the API call is incredibly simple:
curl -sS \
"https://api.truto.one/unified/user-directory/users?integrated_account_id=acc_123&limit=100" \
-H "Authorization: Bearer $TRUTO_API_KEY"Regardless of whether acc_123 points to a Salesforce instance, a Zendesk account, or a BambooHR tenant, you receive the exact same normalized JSON response:
{
"result":[
{
"id": "usr_890xyz",
"name": "Jane Doe",
"email": "jane.doe@example.com",
"status": "active",
"roles":[{ "id": "role_admin", "name": "Admin" }],
"created_at": "2023-05-10T14:30:00Z",
"remote_data": { "custom_field_1": "raw provider payload" }
}
],
"next_cursor": "eyJhbGciOi...",
"result_count": 100
}Notice the remote_data object. We always preserve the raw, unmapped payload from the third-party API. If a specific application returns a highly proprietary field that doesn't fit the unified schema, you still have immediate access to it. You get the benefits of normalization without losing the fidelity of the underlying API.
How Truto's Zero-Code Architecture Solves the Long-Tail Problem
Off-the-shelf unified APIs often market themselves as magic bullets. They are not. The reality of API schema normalization is that forcing 50 different platforms into one box usually results in a lowest-common-denominator schema.
Truto operates on a zero-code architecture. Our runtime engine contains zero integration-specific code. No if (provider === 'okta'). Instead, integration behaviors and schema mappings are defined entirely as data using JSONata expressions.
When a request comes in, a generic execution pipeline reads a JSON configuration describing how to talk to the API and evaluates a JSONata expression to translate the data. Adding support for a niche, legacy SaaS app isn't a massive engineering project; it is a configuration exercise.
The Power of the Override Hierarchy
This architecture enables a three-level override hierarchy. This is where traditional unified APIs fail enterprise engineering teams.
If your enterprise customer uses a highly customized instance of Jira and needs to map a custom field to the unified User schema, you don't have to submit a feature request to Truto and wait six months. You can override the JSONata mapping yourself at three levels:
- Platform Base: The default mapping Truto provides.
- Environment Override: Modifications for your entire staging or production environment.
- Account Override: Tweaks for a single specific customer's integrated account.
Beating Rate Limits with SuperQuery
When pulling massive user lists—imagine pulling 50,000 users from a globally distributed enterprise—hitting third-party API rate limits is a painful reality. Pagination logic becomes brittle, and sync jobs fail halfway through.
Truto bypasses this entirely with our SuperQuery capability. By configuring a sync job, Truto pulls the integration data and stores it in a datastore managed by Truto. Your application can then query the normalized user data via the same Unified API that you use for real-time data fetching, no code changes required. You get sub-second response times and completely avoid live third-party API limits.
Use Cases: From Automated Onboarding to User Access Reviews
Having a normalized, queryable graph of users, roles, and licenses across 100+ applications unlocks core product features that would otherwise take years to build:
- Automated User Access Reviews (UARs): Compliance platforms can programmatically pull
UsersandRoleAssignmentsfrom every connected app. Auditors demand state, not just webhook event streams. By querying the apps directly, you prove exactly who had access during the audit window. - Automated Onboarding & Offboarding: IT management tools can orchestrate the employee lifecycle. When an employee is terminated in the HRIS, you can automatically trigger suspend or delete operations across their CRM, ticketing, and developer tools simultaneously.
- Centralized RBAC: Synchronize application permissions by fetching available
Rolesand applying them to users viaRoleAssignments. This enforces least-privilege access across disconnected tools. - License Optimization: Pull data from
LicensesandUtilizationto identify inactive users. Cross-reference active licenses against actual login activity to identify ghost accounts and automatically downgrade licenses.
Stop Building Point-to-Point User Syncs
Every month your engineering team spends building a one-off API connector for a single SaaS vendor is a month they're not spending on your actual product.
Relying solely on SCIM and major IdPs leaves you blind to the reality of modern shadow IT. Hardcoding point-to-point integrations drains your engineering velocity and traps your team in a never-ending cycle of API maintenance.
By leveraging a programmable, config-first unified API, you abstract away the integration layer. You get the flexibility of custom code with the speed of an off-the-shelf product, allowing you to get back to building your core application.
FAQ
- Why can't I just use SCIM or Okta to get a list of all users?
- SCIM is a push-based protocol meant for provisioning, and it only knows about apps connected to the IdP. Since up to 90% of SaaS applications operate outside centralized IT oversight, you must query the end applications directly to get an accurate user list.
- What is the difference between an IdP directory view and an app-level access view?
- IdPs tell you who exists in the primary directory. App-level systems (like CRMs or ticketing tools) have their own admin roles, seats, and permission models. Audits and security reviews require the app-level view to accurately determine privileged access.
- How does a Unified User Directory API handle custom user fields?
- Truto uses a zero-code architecture with a three-level override hierarchy. This allows you to map custom fields from specific customer tenants into the unified schema using JSONata expressions without waiting for vendor roadmap updates.
- How do I handle rate limits when pulling user lists across many apps?
- Truto's SuperQuery capability syncs data into a datastore managed by Truto, allowing you to query normalized user data directly from read replicas via SQL-like filters. This completely avoids hitting live third-party API limits during bulk reporting or access reviews.