Unified.to
All articles

How to Prepare HRIS Data for AI Models and RAG with

·

To build AI features on HR data with Unified.to, read employees, compensation, payslips, timeshifts and time off from each customer's HRIS through one HR & Directory API, then assemble training rows or RAG chunks in your own pipeline. Unified.to reads the HRIS at request time and keeps no customer records at rest, so the dataset, feature store and embeddings all live in your infrastructure. This guide is for teams building salary-band, tenure, attrition or cost-forecasting models, and HR assistants.

HR systems don't just differ in UI. They model employees, compensation, payroll, and time differently. Some systems treat compensation as a single current record. Others split it into multiple components. Payroll may arrive as clean payslips, or only as time-based shifts. Leave, termination, and employment status are often represented differently or inconsistently.

For a PM or platform engineer, this creates uncomfortable tradeoffs:

  • Can we trust compensation fields to be comparable across HR providers?
  • Is payroll data current-state or historical?
  • Can we train one model across all customers, or does each integration need special handling?

Many teams solve this by narrowing scope or hardcoding logic per HR vendor. That works early, but it limits who your AI features work for and how confidently you can ship changes.

Unified.to's HR & Directory API maps employees, compensation, payslips, timeshifts and time off from each HRIS into one schema: dates are ISO-8601, enums share one set of values, and IDs link records across objects. Amounts keep their own currency and frequency; converting them is your pipeline's job. Which objects and fields come back depends on the integration.

The same records can ground a retrieval-augmented generation (RAG) assistant, where HR records or policy documents are indexed and retrieved at answer time.

Prerequisites

  • Node.js v18+
  • A Unified.to account with an HR & Directory integration activated
  • Your Unified API key
  • A customer HRIS connectionId

Step 1: Set up your project

mkdir hris-ai-training
cd hris-ai-training
npm init -y
npm install @unified-api/typescript-sdk dotenv

Create a .env file:

UNIFIED_API_KEY=your_unified_api_key
CONNECTION_HRIS=your_customer_hris_connection_id

Step 2: Initialize the SDK

import "dotenv/config";
import { UnifiedTo } from "@unified-api/typescript-sdk";

const { UNIFIED_API_KEY, CONNECTION_HRIS } = process.env;

const sdk = new UnifiedTo({
  security: { jwt: UNIFIED_API_KEY! },
});

All HRIS operations live under the sdk.hris namespace.

Step 3: Which HRIS objects feed an HR model?

Employees (status, type, hire and termination dates, current compensation), payslips (pay history), and optionally timeshifts (hours) and time off (absence). Field coverage varies by integration: compensation entries come back on supported integrations such as Workday, BambooHR and ADP Workforce Now, and payslips on payroll integrations such as ADP Workforce Now, UKG Pro HCM and Xero (as of 19 October 2026). Field-level detail: How to Analyze Compensation and How to Read Employee Payroll History.

Step 4: Fetch employees

Employee lists are paginated using limit and offset. The list endpoint returns a plain array, not a wrapped object.

export async function fetchAllEmployees() {
  const out = [];
  let offset = 0;
  const limit = 100;

  while (true) {
    const page = await sdk.hris.listHrisEmployees({
      connectionId: CONNECTION_HRIS!,
      limit,
      offset,
      sort: "updated_at",
      order: "asc",
    });

    if (!page || page.length === 0) break;

    out.push(...page);
    offset += limit;
  }

  return out;
}

Step 5: Fetch payslips per employee

Payslips can be filtered by employee using user_id on most payroll integrations.

export async function fetchPayslipsForEmployee(employeeId: string) {
  return await sdk.hris.listHrisPayslips({
    connectionId: CONNECTION_HRIS!,
    user_id: employeeId,
    limit: 100,
    offset: 0,
    sort: "updated_at",
    order: "asc",
  });
}

This returns an array of HrisPayslip.

Step 6: Optional — fetch timeshifts

If your use case involves hourly analysis, timeshifts can be fetched and joined by employee_user_id.

export async function fetchTimeshifts(startGte?: string, endLt?: string) {
  return await sdk.hris.listHrisTimeshifts({
    connectionId: CONNECTION_HRIS!,
    limit: 100,
    offset: 0,
    start_gte: startGte ?? "",
    end_lt: endLt ?? "",
  });
}

Not all integrations support timeshifts. Your pipeline should handle a 501 (not supported) as well as an empty list.

Step 7: Normalize employees for training

This step converts raw HRIS records into a stable shape for feature extraction.

export function normalizeEmployee(e: any) {
  return {
    id: e.id,
    status: e.employment_status,
    type: e.employment_type,
    hiredAt: e.hired_at,
    terminatedAt: e.terminated_at,
    compensation: (e.compensation ?? []).map((c: any) => ({
      type: c.type,
      amount: c.amount,
      currency: c.currency,
      frequency: c.frequency,
    })),
  };
}

Do not annualize or convert currencies here. Compensation frequency and currency are explicitly modeled and should remain intact.

Step 8: Build payroll history features

Payslips provide historical labels and trends.

export function aggregatePayroll(payslips: any[]) {
  return payslips.map((p) => ({
    startAt: p.start_at,
    endAt: p.end_at,
    gross: p.gross_amount,
    net: p.net_amount,
    currency: p.currency,
  }));
}

Group by month, quarter, or fiscal period depending on your model needs.

Step 9: Assemble AI-ready records

At this point, you can construct training rows that combine:

  • Current employee attributes
  • Current compensation structure
  • Historical payroll events
  • Optional hours worked or timeoff signals
export function buildTrainingRow(employee: any, payroll: any[]) {
  return {
    employeeId: employee.id,
    status: employee.status,
    employmentType: employee.type,
    compensation: employee.compensation,
    payrollHistory: payroll,
  };
}

These records can be serialized to JSONL, stored in a feature store, or streamed into a training job. Before you store them, drop fields the model doesn't need (request only what you use with the fields parameter), and confirm with your customer that their employees' data may be used for model training.

Optional: Using HRIS Data in a RAG Architecture

Beyond structured model training, normalized HR records can also power RAG-based assistants:

  1. Chunk and embed relevant HR fields (for example, job titles, department descriptions, policy documents linked to roles).
  2. Store embeddings in your vector database with identifiers like connection_id, employee_id, and updated_at.
  3. On a user question ('What is this employee's tenure?' or 'What policies apply to contractors?'), retrieve the relevant chunks before generating the answer. See How to Build a RAG Pipeline for Live SaaS Data.

Unified.to reads and maps the records from each HRIS at request time; chunking, embeddings and vector storage stay in your infrastructure. To keep the index current, subscribe to employee change events on supported integrations (virtual webhooks check at an interval you set, as often as every minute on paid plans).

What should you build next?

Start with one model on one object (for example, attrition from employment status and hire dates), confirm field coverage on your customers' HRIS integrations, then add payslips for pay history.

→ Start your 30-day free trial

→ Book a demo

All articles