Entity Visibility Webhooks

1. Purpose

Two independent mechanisms can make an entity record start or stop appearing in a domain API’s output, without the record itself having been created or deleted in HR Core:

  • An Authorization filter change the record starts or stops matching the filter that decides what a given client is allowed to see.
  • A Historical days (historyDays) boundary crossing — the record’s relevant date moves across the subscription’s retention window, so the record starts or stops counting as “closed.”

Previously, both cases were reported using the entity’s own Insert/Delete operations, which was misleading: a filter or retention-window change is not the same event as a record actually being created or deleted in HR Core, but consumers had no way to tell the two apart from the webhook alone.

The fix: Insert and Delete are now reserved strictly for the entity’s own lifecycle (a genuinely new record, or a genuinely deleted one). Visibility changes caused by authorization or historical-days boundaries are now reported as their own operations — Authorize/Unauthorize and Archive/Unarchive — on the entity’s existing <entity>_CHANGED event (e.g. EMPLOYMENT_CHANGED), giving customers clarity about why a record appeared or disappeared.


2. The four operations

Operation Fires when… Cause
Authorize The record starts matching the client’s authorization filter Authorization
Unauthorize The record stops matching the client’s authorization filter Authorization
Archive The record’s closing date moves outside the historyDays window (becomes “too old” to show) Historical days
Unarchive The record’s closing date moves back inside the historyDays window Historical days

All four are carried on the entity’s normal <entity>_CHANGED event — there is no separate event name for visibility changes.


3. What counts as “closed” — per entity

The historyDays field is the historical retention window, in days, that determines how long a closed record stays visible in that domain’s API output after it becomes closed. Any record whose closing date falls further in the past than currentDate − historyDays stops being returned by the API; a record whose closing date is still within that window continues to be returned normally. Which underlying field(s) supply that closing date differs by entity, and for a few entities it’s a comparison of two fields, taking whichever is more restrictive (i.e. the one that would archive the record sooner):

Entities Closing date rule
Employment, EmploymentExtensions, EmploymentPlannedChanges, EmploymentWorkSchedule, CostAllocation, Person, PersonExtensions, SalaryDetails, WorkPattern dischargeDate field only
Assignment, PersonTimelines, EmploymentTimelines, SalaryDetailsTimelines, PersonExtensionTimelines, EmploymentExtensionTimelines The more restrictive of fields dischargeDate and endDate
LeaveEntitlements, LeaveEntitlementChanges, LeaveHours The more restrictive of fields dischargeDate and leaveEntitlementYear
MaternityLeave, SickLeave The more restrictive of fields dischargeDate and recoveryDate
RoleAssignment endDate field only
EmploymentTypes, JobProfile, JobProfileExtensions, OrganizationUnit, OrganizationUnitExtensions, PayrollClients, Vacancy, Workflow Not affected by historyDays field at all

Example (Employment): today is 23/01/2024; the learning domain has configured historyDays: 90. Employments discharged before 25/10/2023 (e.g. 24/10/2023) are archived; employments discharged on or after 25/10/2023 (e.g. 26/10/2023) are shown.


4. The authorization filter fields

Per the Filter configuration, a filter statement is built from a Field, an Operand (In or NotIn), and a set of Values. The valid Field values are: EmploymentType, Classification, PayrollClientCode, PayrollInstitutionCode, companyCode, employmentTypeName.

This filtering is defined per entity’s own authorization node, not per domain in the sense of “which domain the client is on” — every domain can have authorization configured, it’s the entity’s authorization node that the filter statement is checked against. Three entities are explicitly called out as having no authorization node at all: Job Profile, Organization Unit, and Employment Type — a filter can never apply to these.


5. Interaction rules

  • Insert/Delete take priority over Authorize. When a brand-new entity is created and happens to already match the authorization filter, the webhook sent is Insert — not Authorize. Authorize is reserved for a filter change affecting a record that already existed.
  • An authorization-caused Unauthorize suppresses the accompanying field-level Update. If an update happens to change both the entity’s own data and, at the same time, an authorization-relevant field (e.g. Employment’s employmentType) such that the record now fails the filter, only the Unauthorize operation is sent — no Update webhook for the same field change, because the record is now unauthorized and the update itself is no longer the consumer’s concern.
  • A historical-days-caused Archive/Unarchive does not suppress the accompanying field-level Update. If the change is a discharge-date (or other closing-field) update that also happens to cross the historyDays boundary, both the Archive/Unarchive webhook and a separate Update webhook (carrying the changed field) are sent.
  • No redundant visibility webhooks. If a field changes but the record’s visibility status doesn’t actually flip (it was already authorized and remains authorized, or already archived and remains archived), no Authorize/Unauthorize/Archive/Unarchive webhook is sent — only the ordinary Update for the field, if anything changed.
  • Natural aging still triggers Archive on its own. A record doesn’t need any field to change to become archived — if the system’s periodic (daily) evaluation finds a record has aged past the historyDays window purely due to the passage of time, Archive fires without any preceding data change.

6. Examples (Employment)

Natural aging into historyDays — no config or data change

historyDays is set to 30 days. An Employment discharged on March 1, 2025 crosses the boundary once today’s date reaches April 1, 2025 (31 days later). The daily evaluation detects this and sends EMPLOYMENT_CHANGED / Archive — the API stops showing the entity.

A dischargeDate change pushes a record out of the window

historyDays is 90 days. employment1’s discharge date is January 31, 2025 (60 days before today, April 1, 2025 — still inside the window). The discharge date is then changed to December 22, 2024 (100 days before today — now outside the window). Both of the following are sent:

  • EMPLOYMENT_CHANGED / Archive
  • EMPLOYMENT_CHANGED / Update (carrying the dischargeDate change)

The API stops showing the entity.

A dischargeDate change pulls a record back into the window

Same setup, reversed: the discharge date moves from December 22, 2024 (100 days before today, outside the window) to March 7, 2025 (25 days before today, inside the window). Both are sent:

  • EMPLOYMENT_CHANGED / Unarchive
  • EMPLOYMENT_CHANGED / Update (carrying the dischargeDate change)

The API resumes showing the entity.

An employmentType change pushes a record out via the authorization filter

A filter is configured to show records where employmentType is ABC. employment1 has employmentType: ABC and is visible. employmentType is then changed to YAW, no longer matching the filter. Only this is sent:

  • EMPLOYMENT_CHANGED / Unauthorize

No Update webhook is sent for the employmentType change — per the suppression rule above. The API stops showing the entity.

An employmentType change pulls a record back in via the authorization filter

Same filter (employmentType = ABC). employment1 has employmentType: 123 and is not visible. employmentType is then changed to ABC, now matching. Both are sent:

  • EMPLOYMENT_CHANGED / Authorize
  • EMPLOYMENT_CHANGED / Update (carrying the employmentType change)

(An earlier draft of this scenario used a non-existent operation value here — the correct operation is Authorize.) The API resumes showing the entity.

No webhook when visibility doesn’t actually change

A filter allows employmentType values ABC or YAW. employment1 has employmentType: ABC and is visible; an Authorize webhook was already sent for it previously. employmentType is then changed to YAW — still matching the filter, so visibility never flips. Only this is sent:

  • EMPLOYMENT_CHANGED / Update (carrying the employmentType change)

No redundant Authorize is sent, since the record was already authorized.


7. Scope note

This document illustrates the mechanism using Employment only. The set of entities affected by authorization filtering and by historyDays is broader — see the per-entity closing-date table in section 3 for the historical-days side. The authorization side applies per entity’s own authorization node (see section 4) and is not limited to Employment; Employment is used here purely as a representative, well-documented example.