You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Updated 2026-05-18 — the merge.dev naming convention was pivoted from per-provider (merge_hr_workday, merge_hr_hibob, …) to per-category (merge_hris), with the specific provider stored as config.integration on the connection. The agreed shape is mirrored in the matching backend PRs: Auth#21676 and WE#52929. The design doc (Detailed Implementation > Release 1 > Frontend changes > Consts & Type definitions) is out of date on this point; the spec below supersedes it.
Task
Lay the foundation for all other frontend work: connection name constant, the shared config type, and the local provider map.
One connection per category, regardless of how many merge.dev providers we eventually ship — matches the existing "one integration per category" constraint we already enforce for accounting integrations.
Files:
src/CONST/index.ts — add a single MERGE_HRIS entry to CONST.POLICY.CONNECTIONS.NAME and to HR_CONNECTION_NAMES. Add one entry to CONST.POLICY.CONNECTIONS.ROUTE and to NAME_USER_FRIENDLY ('Merge HR' is fine — the per-provider display name comes from config.integration via MERGE_HR_PROVIDERS, not from this map). Also add a separate CONST.MERGE_HR.APPROVAL_MODE namespace mirroring CONST.GUSTO.APPROVAL_MODE (basic, manager, custom) — kept separate from Gusto's so each integration can diverge if/when its supported modes change.
src/types/onyx/Policy.ts — add the shared MergeHRConnectionConfig type and extend Connections:
/** Merge HR connection data — empty (no data lives outside config today). */typeMergeHRConnectionData=Record<string,never>;/** Shared base for HR integrations (Gusto, Merge HR). Pull `finalApprover` + offline-feedback keys * out of the existing GustoConnectionConfig so both extend a common base. */typeHRConnectionConfigBase=OnyxCommon.OnyxValueWithOfflineFeedback<{finalApprover: string|null;// ... other shared fields ...},'approvalMode'|'finalApprover'>;typeMergeHRConnectionConfig=HRConnectionConfigBase&{/** Approval mode — uses its own enum, kept separate from Gusto's to allow divergence. */approvalMode: ValueOf<typeofCONST.MERGE_HR.APPROVAL_MODE>|null;/** Merge integration slug identifying the connected HR system. Strongly typed to the keys * of MERGE_HR_PROVIDERS so adding a provider lights up the union automatically. */integration: keyoftypeofMERGE_HR_PROVIDERS;};typeConnections={// ... existing entries (gusto, zenefits, qbo, etc.) ...[CONST.POLICY.CONNECTIONS.NAME.MERGE_HRIS]: Connection<MergeHRConnectionData,MergeHRConnectionConfig>;};
Refactor GustoConnectionConfig to extend the same HRConnectionConfigBase (small cleanup that drops duplication and makes the parallel shape explicit).
src/CONST/MERGE_HR_PROVIDERS.ts — the single source of truth for display info about each supported Merge HRIS provider (slug → { displayName, iconUrl }). The connection name itself is no longer per-provider, so this map is purely a display lookup:
Resolving a connected policy to its provider becomes a one-liner: MERGE_HR_PROVIDERS[policy.connections.merge_hris?.config?.integration].
iconUrl values are fetched once at design time from Merge's Integration Metadata API and committed here. The images themselves load from Merge's CDN at render time — we don't bundle them. The CheckMergeHRIntegrations Bedrock job (B13) will catch any drift from Merge's published metadata.
What's intentionally out of scope
Multiple Merge connections per policy. One merge_hris per workspace at a time, parallel to the existing accounting-integration constraint. If a workspace ever wants e.g. Workday for US employees + HiBob for UK employees, that's a future enhancement.
Other Merge categories (ATS, accounting). When they arrive, they get their own connection name (merge_ats, merge_accounting) and a parallel MERGE_ATS_PROVIDERS.ts map. The convention this issue establishes scales by adding sibling files, not by extending this one.
Open question for the implementer
Per-provider routes (/workspaces/$id/hr/workday, /hr/hibob, …) vs. a single category-level route (/hr/merge-hris)? Better deeplinking + back-button UX in the per-provider case, but more routes for one underlying connection. Worth confirming with @dannymcclain / @twisterdotcom before implementing. The existing Gusto pattern uses the connection name as the route slug, which would translate to a single /hr/merge-hris here — the simplest answer.
Dependencies
None. Blocks most other frontend issues (F02, F03, F05, F06, F08), so should land first.
Note
Updated 2026-05-18 — the merge.dev naming convention was pivoted from per-provider (
merge_hr_workday,merge_hr_hibob, …) to per-category (merge_hris), with the specific provider stored asconfig.integrationon the connection. The agreed shape is mirrored in the matching backend PRs: Auth#21676 and WE#52929. The design doc (Detailed Implementation > Release 1 > Frontend changes > Consts & Type definitions) is out of date on this point; the spec below supersedes it.Task
Lay the foundation for all other frontend work: connection name constant, the shared config type, and the local provider map.
The agreed policy JSON shape:
{ "connections": { "merge_hris": { "config": { "integration": "workday", "approvalMode": "APPROVAL_ADVANCED", "finalApprover": "alice@acme.com" } } } }One connection per category, regardless of how many merge.dev providers we eventually ship — matches the existing "one integration per category" constraint we already enforce for accounting integrations.
Files:
src/CONST/index.ts— add a singleMERGE_HRISentry toCONST.POLICY.CONNECTIONS.NAMEand toHR_CONNECTION_NAMES. Add one entry toCONST.POLICY.CONNECTIONS.ROUTEand toNAME_USER_FRIENDLY('Merge HR'is fine — the per-provider display name comes fromconfig.integrationviaMERGE_HR_PROVIDERS, not from this map). Also add a separateCONST.MERGE_HR.APPROVAL_MODEnamespace mirroringCONST.GUSTO.APPROVAL_MODE(basic,manager,custom) — kept separate from Gusto's so each integration can diverge if/when its supported modes change.src/types/onyx/Policy.ts— add the sharedMergeHRConnectionConfigtype and extendConnections:Refactor
GustoConnectionConfigto extend the sameHRConnectionConfigBase(small cleanup that drops duplication and makes the parallel shape explicit).src/CONST/MERGE_HR_PROVIDERS.ts— the single source of truth for display info about each supported Merge HRIS provider (slug →{ displayName, iconUrl }). The connection name itself is no longer per-provider, so this map is purely a display lookup:Resolving a connected policy to its provider becomes a one-liner:
MERGE_HR_PROVIDERS[policy.connections.merge_hris?.config?.integration].iconUrlvalues are fetched once at design time from Merge's Integration Metadata API and committed here. The images themselves load from Merge's CDN at render time — we don't bundle them. TheCheckMergeHRIntegrationsBedrock job (B13) will catch any drift from Merge's published metadata.What's intentionally out of scope
merge_hrisper workspace at a time, parallel to the existing accounting-integration constraint. If a workspace ever wants e.g. Workday for US employees + HiBob for UK employees, that's a future enhancement.merge_ats,merge_accounting) and a parallelMERGE_ATS_PROVIDERS.tsmap. The convention this issue establishes scales by adding sibling files, not by extending this one.Open question for the implementer
Per-provider routes (
/workspaces/$id/hr/workday,/hr/hibob, …) vs. a single category-level route (/hr/merge-hris)? Better deeplinking + back-button UX in the per-provider case, but more routes for one underlying connection. Worth confirming with @dannymcclain / @twisterdotcom before implementing. The existing Gusto pattern uses the connection name as the route slug, which would translate to a single/hr/merge-hrishere — the simplest answer.Dependencies
None. Blocks most other frontend issues (F02, F03, F05, F06, F08), so should land first.
Issue Owner
Current Issue Owner: @mhawryluk