SysEngineering

Okta Entity Status

Sets the custom Okta profile attribute entityStatus on every active user — forProfit or nonProfit — derived from the domain of their Okta login.

Status: complete, run live 2026-05-20. Safe to re-run whenever the entity list changes or a new domain appears; it is idempotent and only writes the genuine delta.

Why

The Schmidt family of entities mixes for-profit and non-profit organizations, and several downstream decisions depend on knowing which is which — app assignment rules, reporting splits, and access policies that should not span the boundary. Okta knew a user’s entity but not its tax status, so that had to be answered by hand every time.

Source of truth is the “Schmidt Entity List + Tax Status” Google Sheet. Rather than wire up a live sheet read for a value that changes once or twice a year, the mapping is a lookup table in the script — 37 domains. When the sheet changes, update DOMAIN_TO_STATUS and re-run.

How it works

  1. Pull every ACTIVE Okta user with full Link-header pagination.
  2. Map each user’s login domain through DOMAIN_TO_STATUS.
  3. Compare against the entityStatus already on the profile.
  4. Write only where the value differs. Users already correct are skipped with no API call, so a second run right after a successful one reports zero writes.

Unmapped domains are left blank and reported — never guessed at. That bucket deliberately covers the Spinning Out / Ignore entities (hLevel, Swift Beat) plus contractors, service accounts, and any domain not yet added to the table.

Values written

forProfit and nonProfit are the enum values on the Okta schema, not the display names. Okta renders “For-Profit” / “Non-Profit” in the admin UI from the schema definition, so the admin console will not match what the script prints.

Setup

cd integrations/okta_entity_status
npm install
cp .env.example .env     # then fill in OKTA_DOMAIN and OKTA_API_TOKEN

The token needs profile-edit rights (Super Admin or User Admin). Revoke it when the run is finished — house rule.

Before the first live run: add the attribute to every User Type

Okta scopes custom profile attributes to each User Type’s schema, not to the org. If entityStatus is missing from a given User Type, every write against a user of that type fails with:

Property name 'entityStatus' is not defined in profile

The dry run groups failures by User Type precisely so you can see which schemas need the attribute added first (Directory → Profile Editor → that User Type). Do that before going live, or a whole class of users silently no-ops.

Run

npm run dry-run     # pulls users, joins to the lookup table, prints payloads — no writes
npm run run-live    # writes the updates

Maintaining the lookup table

DOMAIN_TO_STATUS lives near the top of sync-entity-status.js and is keyed by login domain. To add an entity, add its domain(s) with the matching enum value and re-run — no other change needed.

Intentionally absent, and reported as unmapped rather than mapped:

This mirrors EXCLUDE_ENTITIES in okta_profile_sync and SPINOUT_ENTITIES in okta_license_cleanup. If an entity’s status changes in one of those, check the other two.

Output

Every run writes a JSON audit log and a Confluence-ready markdown report next to the script, covering: the write summary, per-User-Type failures, the unmapped bucket with counts by domain, and the full per-user detail. Both contain employee data and are gitignored.