DataSheets — a governed, file-free HR source
DataSheets let you run an in-product, spreadsheet-style table as an authoritative HR source that drives Joiner/Mover/Leaver (JML) — without paying for an HRIS API integration (Paycom, Workday, etc.) or standing up SFTP drops and file watchers.
A DataSheet is governed like any other source: RBAC + per-sheet fine-grained access, soft-delete with retention, full audit, and precedence-aware merge into the identity source of truth.
When to use it. A customer can't (or won't) integrate their HRIS API/SFTP, but still needs JML automation. Maintain employees as rows in a DataSheet; the unified HR sweep treats it exactly like Workday/Paycom and provisions downstream (AD/Entra/SCIM).
Concepts
| Term | Meaning |
|---|---|
| DataSheet | A table with admin-defined columns and rows (the "spreadsheet"). |
| Column | colKey (immutable data key) + Label (display name) + type + an optional canonical mapping. |
| Canonical mapping | Maps a column to an identity attribute (e.g. employeeId, email, firstName). |
| HR source | An HR_SOURCE integration with provider = DATASHEET that reads one sheet. |
| employeeId | The required join key — the column mapped to employeeId matches rows to identities. |
1. Create a DataSheet and define columns
- Go to Admin → DataSheets and click + New to create a sheet.
- Add columns (
col_key,Label, type) and set the mapping for each:- Core identity:
employeeId(required join key),email,firstName,lastName,displayName,department,jobTitle,manager,status. - Standard profile:
username,phoneNumber,mobileNumber,company,streetAddress,city,stateProvince,postalCode,country,preferredLanguage,timezone. - Custom…: any other attribute name you type — carried into the user's governed
metadata(precedence-tracked), available to downstream claim/SCIM mappings.
- Core identity:
- A column mapped to
employeeIdis mandatory — it's the HR-sync join key. Rows without a non-blankemployeeIdare skipped. - Add rows (one per employee). Each row has a lifecycle status (
ACTIVE/INACTIVE).
Spreadsheet grid features
On the grid (with edit rights), hover a column header for inline controls:
- Rename label (✎) — edit the display label; the
col_keyand mapping stay unchanged. - Move left / right (‹ ›) — reorder columns.
- Hide (👁) — hide a column from view (re-show via the Columns button).
- Filter — a per-column, case-insensitive contains filter row.
- Import / Export — CSV round-trip (formula-injection-safe).
2. Register the DataSheet as an HR source
- Go to Admin → HR Sources → Add HR source.
- Pick the DataSheet provider.
- In Connection → DataSheet, search and select the sheet (by name).
- Save. The source is now part of the unified HR sweep.
You can run the sweep on demand (Sync now) or on the scheduled cadence. Every active
HR_SOURCE integration is processed in one sweep.
Joiner / Mover / Leaver
| Event | DataSheet action | Result |
|---|---|---|
| Joiner | Add a row with a new employeeId (status ACTIVE). | New user_account; onJoiner fires (downstream provisioning). |
| Mover | Change a row's mapped values. | onMover fires; changed attributes propagate. |
| Leaver | Set the row's status to INACTIVE (or a terminated status). | onLeaver fires; downstream deprovision. |
The sweep is idempotent — re-running with no changes reports
0 updated.
Scheduling & event-driven sync
A DataSheet HR source can sync three ways — they stack:
| Mode | Trigger | Where to configure |
|---|---|---|
| Global sweep | Coarse cron for all HR sources (default every 6h, HR_SYNC_CRON). | Always on; safety net. |
| Per-source schedule | This source's own cron or interval (e.g. every 15 min). | HR Sources → ⋯ → Schedule… |
| Sync on edit | A row add/update/delete/import marks the source due now; it runs within ~1 minute (a fine-grained ticker, HR_SYNC_TICK_MS). Bursts of edits coalesce into one run. | Schedule… → Sync on edit toggle (default on) |
In HR Sources → ⋯ → Schedule… you set:
- Scheduled sync enabled — master switch for the per-source ticker (the global sweep still covers it).
- Sync on edit — re-sync shortly after a DataSheet change, without waiting for a cron tick.
- Cadence — On-edit / manual only, Every N minutes, or a 6-field cron (
sec min hour dom mon dow).
The dialog also shows last run / next run / last status. Run a single source immediately with ⋯ → Sync now (the page-level Sync now runs every source).
API:
GET|PUT /admin/api/hr-sources/{id}/schedule(permHR_SYNC:READ/HR_SYNC:MANAGE),POST /admin/api/hr-sources/{id}/syncto run one source now.
3. Precedence when multiple sources overlap
When several HR sources can set the same attribute, the HR Source Precedence screen decides the winner per attribute (highest first; a higher source is never overwritten by a lower one — lower sources only fill gaps). Provenance (which source last set each field) is recorded on the user.
DataSheets are rankable at two granularities:
- Provider group — a bare
DATASHEETentry ranks all DataSheets together (latest-wins among them). - Per source — each DataSheet HR source has a stable key
DATASHEET:<id>, shown on the precedence screen as a friendly source name → sheet label, so you can rank one sheet above another. A source that no longer exists is flagged "source removed" and can be removed from the order.
Example order: WORKDAY > DATASHEET:Contractors > DATASHEET:Interns > SCIM.
4. Access control (RBAC + fine-grained)
Coarse (global) roles — assign via the admin Roles UI or an Entra App Role (value = the role name):
| Role | Capability |
|---|---|
DATASHEET_READER | View sheets (read-only) |
DATASHEET_EDITOR | Edit rows/columns of any sheet |
DATASHEET_CREATOR | Create sheets (owns + edits what it creates) |
DATASHEET_REMOVE | Delete / restore / purge sheets |
DATASHEET_ADMINISTRATOR | Full control |
Underlying permissions: DATA_SHEET:READ | CREATE | UPDATE | DELETE | MANAGE.
Fine-grained (per-sheet) — the Access dialog on a sheet grants a specific user
VIEWER / EDITOR / OWNER on just that sheet (ReBAC), layered on top of the global role.
A user with global READ plus a per-sheet EDITOR grant can edit only that sheet.
5. Lifecycle, retention, and audit
- Soft-delete: deleting a sheet moves it to the recycle bin and stops it syncing. Choose a retention window (0–90 days); it's permanently purged after that and is restorable until then.
- Dependency check: deleting a sheet warns which HR sources read it (they stop syncing; their precedence entries become orphaned).
- Audit: every operation (create / update / rename / reorder / soft-delete / restore / purge / column & row changes / grants / CSV import & export) is written to the tamper-evident audit log with the acting admin.
Downstream provisioning
A DataSheet joiner/mover/leaver flows through the same JML pipeline as any HR source, so it provisions to Entra (Microsoft Graph), on-prem AD / LDAP, and SCIM targets exactly like Workday/Paycom — no files, SFTP, or watcher required.
Provisioning is date- and event-driven, not on a separate cron: the lifecycle scheduler creates
the account ahead of the start date (lead time PROVISIONING_PRESTART_LEAD_DAYS, default 6 days),
enables it on the start date, and disables it on termination / leaver — so map startDate
and terminationDate columns to drive the full lifecycle.
Provisioning to Active Directory / Azure AD
AD/LDAP targets are the Directory Integrations you configure under Admin → Directory Integrations. To make one a provisioning target for HR sources:
- Set its Sync direction to One-way Export or Two-way.
- Enable Provision from HR sources (Joiner/Mover/Leaver) on the Sync tab.
Then any DataSheet (or other HR source) joiner enqueues an AD create + enable on the start date,
and a leaver enqueues a disable — drained by the directory sync agent (the broker never
connects inward to a domain controller). Multiple AD targets can be driven at once. Entra is a
parallel opt-in (provisionOnHrSync on the Microsoft Graph config).
Directory Integrations also honor their own auto-sync interval (
autoSyncEnabled+syncIntervalMinutes) for inbound import, run by a ticker (DIRECTORY_SYNC_TICK_MS).