Unified.to
All articles

How to Detect Employee Onboarding and Offboarding Events with

·

To detect new hires and leavers across HR systems with Unified.to's HR & Directory API, compare each employee's employment_status, hired_at and terminated_at between reads, and use employee webhooks where the integration sends them. This guide is for teams automating onboarding, offboarding and access changes. Lifecycle automation breaks when products treat onboarding and offboarding as one-time events instead of state transitions.

Across HR systems, employee lifecycle data is fragmented. Start dates, termination dates, employment status, and organizational attributes are updated independently and often asynchronously. Some systems emit explicit events. Others only report current state. Many require teams to reconstruct lifecycle changes by comparing snapshots or relying on vendor-specific semantics.

For product teams, this creates real operational risk:

  • New hires aren't provisioned on time
  • Departed employees retain access longer than intended
  • Role or team changes fail to trigger downstream updates
  • 'Joiners' and 'leavers' are detected inconsistently across HRISs

Many tools attempt to solve this by inventing lifecycle event objects or relying on undocumented vendor behavior. That approach is brittle and hard to audit.

Unified.to's HR & Directory API doesn't invent lifecycle events. It returns observable employee state (employment status, start and end dates, groups and locations) and, where the integration supplies it, an updated_at timestamp. Lifecycle events are derived by comparing state over time, using consistent semantics across HR systems.

This guide shows how to detect joiners, leavers and other employee changes with Unified.to's HR & Directory API, without vendor-specific logic or inferred history.

What does lifecycle detection cover here?

This article focuses on detecting and reacting to employee lifecycle changes, not performing provisioning directly.

We will:

  • Identify new employees (onboarding)
  • Identify exited employees (offboarding)
  • Detect changes to active employees that require updates
  • Keep downstream systems current incrementally

We will not:

  • Model role history or promotions
  • Infer lifecycle events from payroll or benefits
  • Require webhooks to function correctly
  • Define provisioning logic for downstream systems

Unified.to provides the lifecycle signal. Your product decides how to act on it.

Does Unified.to send onboarding and offboarding events?

Not as such. There's no "lifecycle event" object; on supported integrations, employee created, updated and deleted webhooks tell you a record changed, and the lifecycle meaning comes from the employee's state:

  • Onboarding → employee appears with employment_status = ACTIVE and a hired_at date
  • Offboarding → employment_status transitions or terminated_at is set
  • Updates → any meaningful change reflected in updated_at

This model is intentional. It avoids vendor-specific event semantics and keeps lifecycle detection consistent across HRISs.

Objects you'll use

Employees (HrisEmployee)

Employees are the source of truth for lifecycle state.

Relevant fields:

  • id
  • employment_status
  • hired_at
  • terminated_at
  • updated_at
  • groups[]
  • locations[]
  • company_id

Important constraints:

  • Lifecycle signals are represented as state, not events
  • updated_at reflects changes to the employee record
  • Not all HRISs update all fields simultaneously

Groups (HrisGroup)

Groups provide organizational context.

Relevant fields:

  • id
  • name
  • parent_id
  • type
  • company_id

Groups are useful for assigning onboarding or offboarding steps by department or team.

Locations (HrisLocation)

Locations provide geographic context.

Relevant fields:

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

Locations are often used to trigger jurisdiction-specific onboarding or exit steps.

Companies (HrisCompany)

Companies define scope.

Relevant fields:

  • id
  • name

This allows lifecycle syncing in multi-entity organizations without assuming a single global org.

Step 1: Perform an initial employee ingest

Start by ingesting the current employee population.

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

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

async function fetchEmployees(connectionId: string) {
  const employees = [];
  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',
        'hired_at',
        'terminated_at',
        'updated_at',
        'groups',
        'locations',
        'company_id',
      ].join(','),
    });

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

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

  return employees;
}

This establishes a baseline state.

Step 2: Detect onboarding (joiners)

A new employee is typically identified by:

  • A previously unseen id
  • An employment_status of ACTIVE
  • A populated hired_at date
function detectJoiners(
  previous: Map<string, any>,
  current: any[]
) {
  return current.filter(
    (e) =>
      !previous.has(e.id) &&
      e.employment_status === 'ACTIVE'
  );
}

These employees can trigger onboarding steps such as:

  • Account creation
  • Training assignment
  • Welcome communications

Step 3: Detect offboarding (leavers)

Offboarding is detected through state change.

Common signals:

  • employment_status transitions to INACTIVE
  • terminated_at becomes populated
function detectLeavers(
  previous: Map<string, any>,
  current: any[]
) {
  return current.filter((e) => {
    const prev = previous.get(e.id);
    if (!prev) return false;

    return (
      prev.employment_status === 'ACTIVE' &&
      e.employment_status !== 'ACTIVE'
    );
  });
}

These records can drive:

  • Access revocation
  • Device or account cleanup
  • Exit steps

Step 4: Detect updates to active employees

Not all lifecycle-relevant changes are joiners or leavers.

Examples:

  • Department changes
  • Location changes
  • Company reassignment

Where the integration returns updated_at, you can detect records that need re-evaluation; elsewhere, compare the fields you care about.

function detectUpdates(
  previous: Map<string, any>,
  current: any[]
) {
  return current.filter((e) => {
    const prev = previous.get(e.id);
    if (!prev) return false;

    return e.updated_at !== prev.updated_at;
  });
}

These updates often require partial downstream changes rather than full onboarding or offboarding.

Step 5: Re-read only changed employees where you can

Where an integration supports the updated_gte filter, such as SAP SuccessFactors, Personio, Okta and Microsoft Entra ID, you can re-read only changed employees. Elsewhere, list all employees and compare.

Typical pattern:

  1. Persist last processed updated_at
  2. Re-fetch employees with updated_gte
  3. Compare state against stored snapshot
  4. Derive joiners, leavers, and updates

Can webhooks replace polling?

On supported integrations, yes, for detection: employee events are native on HiBob, Personio, FactorialHR, Remote.com, Google Workspace Directory and Microsoft Entra ID (including deletes), and come from virtual webhooks on BambooHR, Workday, SAP SuccessFactors, UKG Pro HCM and Okta, which check at an interval you set, as often as every minute on paid plans (as of 20 October 2026). ADP Workforce Now and Deel send no employee events.

However:

  • Webhooks are an optimisation, not a requirement
  • Polling with updated_at remains the baseline approach
  • Lifecycle logic should not depend on webhook guarantees

This ensures correctness even when webhooks are unavailable or delayed.

Can I provision accounts from the same data?

Yes. Use the lifecycle signal to create or remove accounts in your own product, or write to a directory: Okta, Microsoft Entra ID, Google Workspace Directory and JumpCloud create, update and remove employees through the same API, and Unified.to's SCIM API offers SCIM Users and Groups over its HR integrations. Device records (asset tag, model, operating system, assigned users) are readable on integrations such as Microsoft Entra ID and Google Workspace Directory (as of 19 October 2026).

To keep a current copy of employees in your own database, Database Sync (Grow, Pro and Scale) writes changes into MongoDB, MySQL, Postgres, MSSQL, MariaDB, Supabase or Snowflake for objects with native or virtual webhooks.

What doesn't Unified.to assume?

Unified.to does not:

  • Emit explicit onboarding or offboarding events
  • Infer lifecycle changes from payroll or benefits
  • Model role or title history
  • Guarantee synchronous field updates across HRISs

It returns observable state changes and leaves lifecycle interpretation to your product.

How do I start?

Employee lifecycle syncing is about state awareness, not event orchestration.

Unified.to's HR & Directory API reads employee state from the source at request time (start dates, termination dates, groups, locations and change timestamps where supplied) across 679 HR & Directory integrations, so onboarding and offboarding steps follow the HR system.

With explicit state and incremental detection, lifecycle automation becomes reliable instead of reactive.

→ Start your 30-day free trial

→ Book a demo

All articles