Unified.to
All articles

How to Analyze Employee Population for Compliance and Risk (GRC) with Unified's HR & Directory API


February 2, 2026

Employee population analysis for compliance and risk fails when tools blur the line between what is observable and what is inferred.

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's HR & Directory API is designed to support population analysis without guessing. It exposes only what the underlying HRIS explicitly provides, with consistent schemas and clear boundaries around what is current-state, optional, or unavailable. Aggregation is possible. Interpretation is left to you.

This guide shows how to analyze employee population data for compliance and risk use cases—workforce composition, demographic distribution, organizational concentration, and geographic exposure—using Unified's HR & Directory API, without reconstructing history or inferring protected attributes.

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 provides the data layer. Compliance interpretation belongs to your governance framework.

The mental model: population analysis is descriptive, not judgmental

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:

  • id
  • employment_status
  • employment_type
  • gender (optional; self-reported where supported)
  • date_of_birth
  • marital_status
  • hired_at
  • terminated_at
  • groups[]
  • locations[]
  • company_id
  • updated_at

Important constraints:

  • Demographic fields may be missing or null.
  • Gender values are enumerated but not required.
  • No assumptions should be made when attributes are absent.

Groups (HrisGroup)

Groups support organizational segmentation.

Relevant fields:

  • id
  • name
  • parent_id
  • type
  • is_active
  • company_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:

  • id
  • name
  • address.region
  • address.country
  • is_active
  • company_id

Location data is essential for region-specific compliance and exposure reporting.

Companies (HrisCompany)

Companies define reporting scope.

Relevant fields:

  • id
  • name

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 refresh only changed records in production systems.

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: Track population changes over time

Use hired_at, terminated_at, and updated_at to observe change.

Examples:

  • Net population growth by quarter
  • Attrition concentration by department
  • Demographic shifts by region

These are observed trends, not predictions.

What Unified does not infer

Unified intentionally does not:

  • Infer ethnicity or protected attributes
  • Reconstruct employment or pay history
  • Normalize missing demographic data
  • Make compliance judgments

This keeps population analysis auditable and legally defensible.

Closing thoughts

GRC-oriented employee analysis depends on restraint as much as insight.

Unified's HR & Directory API provides a clean, normalized foundation for workforce population analysis—exposing only what HR systems explicitly record, preserving missing data where it exists, and leaving interpretation to governance frameworks and human judgment.

That clarity is what makes compliance analysis trustworthy.

Start your 30-day free trial

Book a demo

All articles