How to Analyze Employee Population for Compliance and Risk (GRC) with
·
To analyze your workforce for governance, risk and compliance (GRC), read employees with their employment type, demographics (where recorded), groups, locations and company through Unified.to's HR & Directory API, and count them by segment, keeping missing values as their own category. This guide is for GRC and compliance-tooling teams who need descriptive workforce data they can defend in an audit; it doesn't infer protected attributes or make compliance judgments.
Across HR systems, demographic attributes, employment classifications, and compensation data vary widely in availability and meaning. Some fields are optional. Some are self-reported. Some exist only for certain geographies or company policies. When products attempt to normalize or infer missing attributes, they introduce legal, audit, and reputational risk.
For governance, risk, and compliance (GRC) teams, the challenge isn't lack of data. It's trusting the analysis.
Unified.to's HR & Directory API returns only what the HRIS records, in one schema; a field the HRIS doesn't provide comes back empty, not estimated. Aggregation is possible. Interpretation is left to you.
What GRC analysis means in this context
This article focuses on population analysis, not compliance determination.
We will:
- Analyze workforce composition and distribution
- Segment populations by observable attributes
- Surface structural risk indicators (concentration, imbalance, exposure)
We will not:
- Declare regulatory compliance
- Detect bias or discrimination
- Infer protected characteristics
- Produce legal conclusions
Unified.to provides the data layer. Compliance interpretation belongs to your governance framework.
What questions can population analysis answer?
GRC-oriented employee analysis should answer questions like:
- How is our workforce distributed by role, location, and employment type?
- Which organizational units concentrate certain demographic attributes?
- Where do optional or sensitive attributes cluster—or go missing?
- How does workforce composition differ across entities or regions?
These are descriptive questions. They rely on counts, distributions, and segmentation—not inference.
Objects you'll use
Employees (HrisEmployee)
Employees are the foundation of population analysis.
Relevant fields:
idemployment_statusemployment_typegender(optional; values MALE, FEMALE, INTERSEX, TRANS, NON_BINARY)date_of_birthmarital_statushired_atterminated_atgroups[]locations[]company_idupdated_at
Important constraints:
- Demographic fields are returned on some integrations only (gender and date of birth on Workday, BambooHR, HiBob, Personio and SAP SuccessFactors, for example; marital status on few), and may be empty for individual employees.
- Gender values are enumerated but not required.
- No assumptions should be made when attributes are absent.
Groups (HrisGroup)
Groups support organizational segmentation.
Relevant fields:
idnameparent_idtypeis_activecompany_id
Groups allow rollups by department, division, or business unit without assuming hierarchy implies reporting structure.
Locations (HrisLocation)
Locations enable geographic and jurisdictional analysis.
Relevant fields:
idnameaddress.regionaddress.countryis_activecompany_id
Location data is essential for region-specific compliance and exposure reporting.
Companies (HrisCompany)
Companies define reporting scope.
Relevant fields:
idname
This supports entity-level compliance views for multi-subsidiary organizations.
Step 1: Fetch the employee population
Employee lists are paginated and support incremental refresh.
import { UnifiedTo } from '@unified-api/typescript-sdk';
const sdk = new UnifiedTo({
security: { jwt: process.env.UNIFIED_API_KEY! },
});
async function fetchEmployees(connectionId: string) {
const results = [];
let offset = 0;
const limit = 100;
while (true) {
const page = await sdk.hris.listHrisEmployees({
connectionId,
limit,
offset,
sort: 'updated_at',
order: 'asc',
fields: [
'id',
'employment_status',
'employment_type',
'gender',
'date_of_birth',
'marital_status',
'groups',
'locations',
'company_id',
].join(','),
});
if (!page || page.length === 0) break;
results.push(...page);
offset += limit;
}
return results;
}
Use updated_gte to read only changed records on integrations that support it, or employee change events on supported integrations.
Request only the fields your analysis needs with the fields parameter (as in the example above). The Employee model can include sensitive fields such as date of birth and national ID; if you don't request them, they never reach your system. Unified.to keeps no customer records at rest, so what you store is the only copy outside the HRIS.
Step 2: Define the population baseline
Most GRC analyses focus on the current workforce.
function activePopulation(employees: any[]) {
return employees.filter(
(e) => e.employment_status === 'ACTIVE' && !e.terminated_at
);
}
Inclusion rules (e.g., contractors vs full-time employees) should be explicit and configurable.
Step 3: Segment by observable attributes
Employment type distribution
function byEmploymentType(employees: any[]) {
const counts: Record<string, number> = {};
for (const e of employees) {
const type = e.employment_type ?? 'UNKNOWN';
counts[type] = (counts[type] ?? 0) + 1;
}
return counts;
}
This is useful for identifying workforce composition risk, such as over-reliance on contractors in regulated roles.
Gender distribution (where available)
function genderDistribution(employees: any[]) {
const counts: Record<string, number> = {};
for (const e of employees) {
const gender = e.gender ?? 'UNSPECIFIED';
counts[gender] = (counts[gender] ?? 0) + 1;
}
return counts;
}
Important:
- Missing values are preserved as a category.
- No assumptions are made about unspecified records.
Age bands
function ageBands(employees: any[]) {
const now = new Date();
const bands = { '<30': 0, '30–50': 0, '50+': 0, UNKNOWN: 0 };
for (const e of employees) {
if (!e.date_of_birth) {
bands.UNKNOWN++;
continue;
}
const age =
(now.getTime() - new Date(e.date_of_birth).getTime()) /
(365.25 * 24 * 60 * 60 * 1000);
if (age < 30) bands['<30']++;
else if (age <= 50) bands['30–50']++;
else bands['50+']++;
}
return bands;
}
Age analysis is useful for workforce planning and regulatory exposure, but should always be treated as descriptive.
Step 4: Segment by org unit and location
Because employees reference groups and locations directly, segmentation is straightforward.
Examples:
- Gender distribution by department
- Employment type by region
- Workforce concentration by company entity
Rollups are performed client-side using the same joins across HRISs.
Step 5: How do you track population changes?
Use hired_at, terminated_at and updated_at the same way as in How to Forecast Headcount and Hiring Needs; these are observed trends, not predictions.
What does Unified.to not infer?
Unified.to intentionally does not:
- Infer ethnicity or protected attributes
- Reconstruct employment or pay history
- Normalize missing demographic data
- Make compliance judgments
That keeps every number traceable to a field the HRIS recorded.
Next steps
GRC-oriented employee analysis depends on restraint as much as insight.
Confirm which demographic fields your customers' HRIS integrations return (Feature Support tab), request only those fields, and keep "unknown" as a reported category. Related: HR API Integration · Employee data model.