Skip to Content
Product GuideInput format: the people export

Input format: the people export

Run the commands below from the installation directory. See Run operator commands for how ./lens reaches the running installation and copies files into it.

The people export feeds the sealed identity joinSealed identity joinStored links between employee fingerprints and teams or services. The fingerprints replace readable employee identifiers. Product pages do not show the links for individual people., which powers the key-person check. It is ingestedIngestRead supplied records or documents and add their information to NovusLens, after the required checks. with:

From the installation directory:

./lens connectors people --org harbour --fixture people.json

On success the command prints a summary of what it wrote and exits 0; the map shows the result after the projectionProjectionThe regular pass that brings the read model up to date with the canonical log. It runs every 30 seconds by default. service’s next pass (about 30 s by default). A refusal names what was wrong.

Employee ids and email addresses are hashedHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records. with an installation saltSaltExtra data mixed into a value before its fingerprint is calculated. It makes matching or guessing the original value harder. before they are written. The clear values never land, and no page ever shows a headcount. The one read of the join is the key-person check: a cellCellA box on the map that stands for a team, service, supplier or AI agent. It is a unit you can inspect, not necessarily a department or a person. on the measured critical pathCritical pathThe dependency chain with the greatest total coordination weight in the map. It identifies a structural constraint. It does not state elapsed time. with one to four mapped people is named as a small team on a load-bearing path. No count is shown. Cells with zero mapped people are skipped; this check does not establish missing ownership.

Required fields

  • teams: a list of {team, cell} pairs mapping the HRHRHuman resources: the organisation's people records and the team that maintains them. team name to the cell id in the map.
  • people: a list. Each person needs employee_id, email and team. A person whose team is not in teams is reported as unknown-team and skipped.

Optional fields

  • export_id: the name of this export, recorded against every person it places. Required when complete is true.
  • complete: true declares that this file is the whole current membership of the teams it lists. Omit it, or set false, for an incremental file.

What a re-run does

An export does not replace the previous one by name. It updates people one by one, and what happens to a person the file does not mention depends on complete.

SituationIncremental (default)Complete snapshot ("complete": true)
A person in the file, not seen before (joiner)Added to their team’s cellAdded to their team’s cell
A person in the file with a new team (mover)Moved; the latest team winsMoved; the latest team wins
A person in the file, unchangedRefreshed; one row per person, never twoRefreshed; one row per person, never two
A person not in the file, in a team the file lists (leaver)Untouched until the retention limitWithdrawn now: no read sees them again, the row stays for the audit
A person in a team the file does not listUntouchedUntouched: a snapshot covers only the teams it lists
The same file twiceNo changeNo change; nothing withdrawn

So a monthly HR export should be sent with "complete": true and the full list of teams it covers. A file that covers one team, or one department, is still safe to declare complete: only the teams in its teams list are affected. A file sent without complete can never remove anyone.

The command’s summary states which case ran: complete snapshot 'hris-2026-10': 1 withdrawn, or incremental (adds and moves only — nobody removed). Withdrawal ends the person’s retention; nothing is erased, and no page shows a count of who left.

Worked example: one month to the next

September, a complete snapshot of two teams:

{ "export_id": "hris-2026-09", "complete": true, "teams": [ {"team": "Bookings Desk", "cell": "bookings"}, {"team": "Customs Filing", "cell": "customs"} ], "people": [ {"employee_id": "E1001", "email": "a.person@example.org", "team": "Bookings Desk"}, {"employee_id": "E1002", "email": "b.person@example.org", "team": "Customs Filing"}, {"employee_id": "E1003", "email": "c.person@example.org", "team": "Customs Filing"} ] }

October: E1002 has left, E1003 has moved to the bookings desk, E1004 has joined customs filing.

{ "export_id": "hris-2026-10", "complete": true, "teams": [ {"team": "Bookings Desk", "cell": "bookings"}, {"team": "Customs Filing", "cell": "customs"} ], "people": [ {"employee_id": "E1001", "email": "a.person@example.org", "team": "Bookings Desk"}, {"employee_id": "E1003", "email": "c.person@example.org", "team": "Bookings Desk"}, {"employee_id": "E1004", "email": "d.person@example.org", "team": "Customs Filing"} ] }

After October’s file: E1002 is withdrawn, E1003 attributes to bookings, E1004 attributes to customs. The key-person check on either cell reads the October membership. Had October’s file been sent without complete, E1002 would still attribute to customs until the retention limit.

Read more

Last updated