Unified.to
All articles

How to Read Employee Payroll History with Unified's HR API


January 11, 2026

Payroll analysis breaks when products rely on inferred compensation instead of recorded payroll events.

Across HR systems, 'pay history' is often implied rather than explicit. Employee records may show a current salary or hourly rate, but they rarely encode when that compensation applied, which period it covered, or what was actually paid after deductions. Vendors expose this differently, and many don't expose it cleanly at all.

For a PM, this creates real constraints:

  • Can payroll analytics be trusted across HR systems with different data models?
  • Can historical pay be reconstructed without guessing past compensation?
  • Can you explain exactly where a number came from during audits or disputes?

Many products solve this by storing snapshots, reconstructing history, or quietly falling back to current-state compensation fields. That approach works—until it doesn't, and correctness becomes hard to defend.

Unified's HR API takes a stricter stance. It does not invent payroll history or mutate employee records. Instead, it exposes documented payroll artifacts—payslips and timeshifts—when the underlying HR system supports them. These represent point-in-time payroll snapshots and time-window compensation records, respectively.

This guide shows how to assemble employee payroll history using those primitives only—reading payslips and timeshifts, joining them to employees, and aggregating results client-side—without vendor-specific integrations, undocumented fields, or assumed compensation history.

Prerequisites

  • Node.js 18+
  • A Unified workspace
  • An HR integration enabled for your customer
  • A connectionId created via Unified's auth flow
  • A Unified API key

Step 1: Fetch employees

Employees are the anchor object for all HR data. All payroll records reference employees by ID.

import { UnifiedTo } from '@unified-api/typescript-sdk';

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

const connectionId = 'YOUR_CONNECTION_ID';

const employees = await sdk.hris.listHrisEmployees({
  connectionId,
  limit: 100,
  offset: 0,
  sort: 'updated_at',
  order: 'asc',
});

What this returns

  • A plain array of HrisEmployee
  • No wrapper object
  • No cursor field

Pagination is handled with limit and offset. You know you've reached the end when the number of returned records is less than your requested limit.

Employee identifiers

Each employee has a canonical identifier:

employee.id

All payroll objects reference this ID.

Step 2: Fetch payroll history via payslips

Payslips are the primary source of historical payroll data in Unified.

const payslips = await sdk.hris.listHrisPayslips({
  connectionId,
  limit: 100,
  offset: 0,
  user_id: employeeId, // optional filter
  sort: 'updated_at',
  order: 'asc',
});

Payslip semantics

Each HrisPayslip represents a point-in-time payroll snapshot:

  • start_at / end_at: pay period
  • paid_at: payment date
  • gross_amount
  • net_amount
  • currency
  • details[]: earnings, taxes, deductions

Joining payslips to employees

payslip.user_id === employee.id

Date filtering

Payslips cannot be filtered by pay-period dates server-side. To work with a date range:

  • Fetch all payslips
  • Filter client-side using start_at / end_at

Step 3: Fetch time-based compensation via timeshifts (optional)

Some HR systems calculate payroll from worked hours rather than fixed salaries. For these, Unified exposes timeshifts.

const timeshifts = await sdk.hris.listHrisTimeshifts({
  connectionId,
  limit: 100,
  offset: 0,
  start_gte: '2026-01-01T00:00:00Z',
  end_lt: '2026-02-01T00:00:00Z',
});

Timeshift semantics

Each HrisTimeshift represents compensation for a time window:

  • start_at / end_at
  • hours
  • compensation[] entries:
    • amount
    • currency
    • frequency (HOUR, DAY, ONE_TIME, etc.)

Joining timeshifts to employees

timeshift.employee_user_id === employee.id

Timeshifts support true server-side date filtering using start_gte and end_lt, but availability depends on the integration.

Step 4: Understand what is not historical

Unified also exposes employee.compensation[], but:

  • It has no effective dates
  • No history semantics are documented
  • It should be treated as current-state compensation only

For historical payroll analysis, always use payslips or timeshifts.

Step 5: Aggregate payroll data client-side

Unified returns raw numeric values. All aggregation is your responsibility.

Key constraints

  • No currency normalization
    • Each record includes its own currency
    • Aggregate only within the same currency
  • No frequency normalization
    • Timeshift compensation may be hourly, yearly, or one-time
    • Convert units explicitly if needed
  • No server-side aggregation
    • No totals, averages, or group-by endpoints

Example aggregation logic (conceptual)

const totalsByPeriod = {};

for (const payslip of payslips) {
  const periodKey = `${payslip.start_at}-${payslip.end_at}`;
  const currency = payslip.currency;

  if (!totalsByPeriod[periodKey]) {
    totalsByPeriod[periodKey] = { currency, gross: 0, net: 0 };
  }

  totalsByPeriod[periodKey].gross += payslip.gross_amount ?? 0;
  totalsByPeriod[periodKey].net += payslip.net_amount ?? 0;
}

Error handling and unsupported features

Unified uses standard HTTP status codes.

Important behavior

  • 501 Not Implemented
    • The integration does not support that feature (e.g. payslips or timeshifts)
    • The connection itself is still valid
  • 401 Unauthorized
    • Connection is broken and must be recreated
  • 403 Forbidden
    • Missing scopes or permissions

Always check the Feature Support tab for an integration in app.unified.to to confirm object availability.

Summary

With Unified's HR API, you can reliably read employee payroll history by:

  1. Fetching employees
  2. Joining payslips for pay-period payroll snapshots
  3. Optionally joining timeshifts for time-based compensation
  4. Aggregating results client-side with explicit currency and frequency handling

This approach avoids vendor-specific APIs, preserves correctness, and scales cleanly across HR systems—without Unified storing or mutating payroll data.

All articles