Skip to main content

HR Sources

An HR source is an authoritative system of record (Paycom, Gusto, ADP, Workday, Rippling, BambooHR, …) that drives nexusID's joiner/mover/leaver (JML) lifecycle. nexusID pulls the employee directory, upserts identities (matched by employeeId), and fires joiner/mover/leaver into provisioning, deprovisioning, access reviews, and the audit chain.

HR sources are DB-configured through the admin integrations console — no environment variables or redeploys. Each source is an Integration row of type HR_SOURCE:

  1. Admin → Integrations → New, type HR Source, pick the provider (Paycom / Gusto / ADP / Workday / Rippling / BambooHR).
  2. Fill in the provider's config form (rendered from the provider's own schema). Where a vendor supports more than one auth method, an Auth method selector shows only the chosen method's fields. Secrets are encrypted at rest (AES-256-GCM).
  3. Test connection, then activate.

The form, the auth methods, and the credential storage are all schema-driven and DB-backed — adding or editing a source never touches environment variables or requires a redeploy (binding engineering rule C-27: integrations are DB- and Admin-UI-configurable).

Auth methods per vendor​

The form shows only the methods a vendor actually supports — OAuth2 is not forced where it doesn't apply:

VendorAuth method(s) in the UI
BambooHRAPI key
PaycomSFTP (key/password) · REST API (SID + token)
GustoOAuth 2.0 (refresh token)
ADPOAuth 2.0 (client credentials) + mutual-TLS
WorkdayIntegration System User (Basic) · OAuth 2.0
RipplingAPI key · OAuth 2.0

Under the hood, ProviderFactory.createProvider(integration) decrypts the config and initializes the matching HrDirectoryProvider; a single scheduled sweep (HrSourceSyncService) syncs every active HR_SOURCE integration. Adding a vendor is a new row, not a deployment.

HR_SOURCE integrations (DB, encrypted)
→ HrSourceSyncService (one sweep, all sources)
→ ProviderFactory.createProvider() → <Vendor>Provider.fetchRecords()
→ shared upsert (match by employeeId)
→ LifecycleEventService onJoiner/onMover/onLeaver
→ provisioning / deprovisioning + HR source precedence + workflows

Tune the sweep cadence with hr.sync.cron (default every 6h) — that's an operational schedule, not per-source config; the sources and their credentials live in the DB.

How OAuth2 tokens are generated (no SSO)​

HR-source auth is machine-to-machine — not org-IdP SSO. For the OAuth2 vendors you don't paste a short-lived bearer token; nexusID mints and refreshes it itself. On a provider's form, leave the static Bearer Token blank and fill the OAuth fields instead:

  • client_credentials (Workday API client, ADP, …): set the token URL, client ID, and client secret. nexusID exchanges them for an access token before each sync and caches it until just before expiry. No human involved.
  • refresh_token (Gusto, Rippling — authorization-code vendors): an admin authorizes the app once against the vendor (not your org IdP) to obtain a refresh token; store it with the client ID/secret and nexusID exchanges it for access tokens forever after.

Long-lived API keys (BambooHR, Paycom, a Rippling API key) need no OAuth — just the key/secret. Tokens and secrets are encrypted at rest.

ADP mutual-TLS: ADP requires a client certificate on both its token endpoint and its data API. Paste your PKCS#12 keystore as base64 (.p12/.pfx) plus its password into the ADP form — it's stored encrypted in the DB (no filesystem path), and nexusID presents the client cert on every ADP call.

Why DB config (and when to use env)​

DB config is the friendly default: self-service in the UI, encrypted secrets, hot add/disable, no redeploy. The legacy environment-variable connectors (PAYCOM_SYNC_*, GUSTO_SYNC_*, ADP_SYNC_*, WORKDAY_SYNC_*) still exist and are off by default — useful for GitOps / air-gapped deployments that deliberately keep secrets out of the database. Use whichever fits; for most operators the admin console is the way.

Supported sources​

SourceTransportAuthPage
PaycomSFTP CSV · REST APISFTP key/pwd · API SID/tokenPaycom
GustoREST APIOAuth2 (token minted by nexusID)Gusto
ADPREST APIOAuth2 client-credentials + mutual-TLSADP
WorkdayRaaS report · RTS pushBasic (ISU) / OAuth2Workday
RipplingREST APIAPI key / OAuth2Rippling
BambooHRREST APIBasic (API key)BambooHR

Matching, precedence & write-back​

  • Match key: employeeId (the source's stable id). New → joiner; changed → mover; terminated status → leaver.
  • Precedence: when several sources feed the same identity, HR Source Precedence decides which wins per attribute.
  • Outbound write-back: Paycom (terminate/update) and Gusto (terminate) can push nexusID-originated changes back; see each page.

Rejoiners (return to Active)​

When a mover moves an identity's lifecycle status back into Active — a re-hire, a status flip undone, a returning contractor — nexusID re-instates the account on every source it exists in, generically per source, not per-vendor logic:

  • Windows AD / Azure AD DS / LDAP (DIRECT writer): if the account currently sits under the configured leaver OU, it is first moved back to its active OU (OU routing resolves the target) and given a fresh credential (unicodePwd + pwdLastSet=0 on AD family, forcing a change at next logon; userPassword on generic LDAP) — a stale or expired leaver credential is never silently reactivated. Then the ACCOUNTDISABLE bit is cleared on userAccountControl. A freshly-created account already in its active OU skips the move and is just switched on.
  • Microsoft Entra ID: sets accountEnabled=true via Graph. If the account isn't found in that Entra config at all, re-instatement is a no-op and the ordinary provisioning step that follows creates it instead.

This fires from the same ENABLE source action a scheduled start-date provisioning uses (see DataSheets → Downstream provisioning) — a rejoiner is handled as "apply the Active lifecycle state's actions to every configured source," not a special case.