[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7NEB3AQF8N3NJSCDAT9PQ":3,"skill-same:01M6F7NEB3AQF8N3NJSCDAT9PQ":79},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":56,"commitSha":57,"skillDirectory":58,"forksCount":59,"repositoryHtmlUrl":60,"repositoryHomepageUrl":61,"ownerHtmlUrl":62,"ownerBlogUrl":6,"ownerDescription":63,"updatedAt":64,"install":65,"resources":78},"01M6F7NEB3AQF8N3NJSCDAT9PQ","bamboohr-reference-architecture",null,"Implement BambooHR reference architecture for production HR data pipelines.\nUse when designing new BambooHR integrations, building employee sync systems,\nor establishing architecture standards for BambooHR-powered applications.\nTrigger with phrases like \"bamboohr architecture\", \"bamboohr design\",\n\"bamboohr project structure\", \"bamboohr system design\", \"bamboohr pipeline\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbamboohr-reference-architecture\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"bamboohr reference architecture","bamboohr","architecture","reference","read","hr","io","project","structure","standards","system design","design","integrations","system","pipeline","data","data pipelines","code","sync","project structure","2026-11-10 10:12:30",false,"---\nname: bamboohr-reference-architecture\ndescription: |\n  Implement BambooHR reference architecture for production HR data pipelines.\n  Use when designing new BambooHR integrations, building employee sync systems,\n  or establishing architecture standards for BambooHR-powered applications.\n  Trigger with phrases like \"bamboohr architecture\", \"bamboohr design\",\n  \"bamboohr project structure\", \"bamboohr system design\", \"bamboohr pipeline\".\nallowed-tools: Read, Grep\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ntags: [saas, hr, bamboohr, architecture]\ncompatible-with: claude-code\n---\n\n# BambooHR Reference Architecture\n\n## Overview\n\nProduction-ready architecture for BambooHR integrations covering the three most common patterns: real-time employee sync, HR data pipeline, and employee lifecycle automation.\n\n## Prerequisites\n\n- Understanding of layered architecture and event-driven design\n- BambooHR API knowledge from earlier skills in this pack\n- TypeScript project setup with Node.js 18+\n\n## Instructions\n\n### Architecture Overview\n\n```\n┌──────────────────────────────────────────────────────────┐\n│                    Your Application                       │\n├──────────────┬───────────────┬────────────────────────────┤\n│  API Layer   │  Sync Engine  │  Webhook Handler           │\n│  \u002Fapi\u002F*      │  (Cron\u002FQueue) │  \u002Fwebhooks\u002Fbamboohr        │\n├──────────────┴───────────────┴────────────────────────────┤\n│                   Service Layer                            │\n│  EmployeeService  │  TimeOffService  │  ReportService     │\n├───────────────────┴──────────────────┴────────────────────┤\n│                BambooHR Client Layer                       │\n│  BambooHRClient  │  Cache  │  RetryHandler  │  Metrics    │\n├───────────────────┴─────────┴────────────────┴────────────┤\n│                   Data Layer                               │\n│  PostgreSQL (employees)  │  Redis (cache)  │  S3 (files)  │\n└───────────────────────────────────────────────────────────┘\n                           │\n                           ▼\n              ┌────────────────────────┐\n              │   BambooHR REST API    │\n              │ api.bamboohr.com\u002Fapi\u002F  │\n              │   gateway.php\u002F{co}\u002Fv1  │\n              └────────────────────────┘\n```\n\n### Project Structure\n\n```\nbamboohr-integration\u002F\n├── src\u002F\n│   ├── bamboohr\u002F\n│   │   ├── client.ts              # HTTP client (from sdk-patterns)\n│   │   ├── types.ts               # BambooHR API response types\n│   │   ├── retry.ts               # Retry with Retry-After support\n│   │   ├── cache.ts               # LRU + Redis cache layer\n│   │   └── metrics.ts             # Request counting and latency\n│   ├── services\u002F\n│   │   ├── employee-sync.ts       # Incremental directory sync\n│   │   ├── time-off.ts            # PTO balance and request management\n│   │   ├── reports.ts             # Custom report generation\n│   │   └── lifecycle.ts           # Onboarding\u002Foffboarding automation\n│   ├── handlers\u002F\n│   │   ├── webhook.ts             # Webhook signature verification + routing\n│   │   └── events.ts              # Employee change event processors\n│   ├── api\u002F\n│   │   ├── health.ts              # Health check endpoint\n│   │   ├── employees.ts           # REST API for local employee data\n│   │   └── reports.ts             # Report generation endpoints\n│   ├── jobs\u002F\n│   │   ├── full-sync.ts           # Scheduled full directory sync\n│   │   ├── incremental-sync.ts    # Frequent delta sync\n│   │   └── report-export.ts       # Scheduled report export\n│   └── db\u002F\n│       ├── schema.sql             # PostgreSQL schema\n│       └── queries.ts             # Database queries\n├── tests\u002F\n│   ├── unit\u002F\n│   │   ├── client.test.ts\n│   │   ├── employee-sync.test.ts\n│   │   └── webhook.test.ts\n│   ├── integration\u002F\n│   │   └── bamboohr-live.test.ts\n│   └── mocks\u002F\n│       └── bamboohr-handlers.ts   # MSW handlers\n├── config\u002F\n│   ├── default.json\n│   ├── production.json\n│   └── test.json\n└── docker-compose.yml             # PostgreSQL + Redis for local dev\n```\n\n### Step 1: Data Model\n\n```sql\n-- db\u002Fschema.sql\nCREATE TABLE bamboohr_employees (\n  id               INTEGER PRIMARY KEY,  -- BambooHR employee ID\n  first_name       TEXT NOT NULL,\n  last_name        TEXT NOT NULL,\n  display_name     TEXT,\n  work_email       TEXT,\n  job_title        TEXT,\n  department       TEXT,\n  division         TEXT,\n  location         TEXT,\n  supervisor_id    INTEGER REFERENCES bamboohr_employees(id),\n  status           TEXT DEFAULT 'Active',\n  hire_date        DATE,\n  termination_date DATE,\n  employee_number  TEXT,\n  raw_data         JSONB,                -- Full BambooHR response\n  synced_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n  created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n  updated_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX idx_employees_status ON bamboohr_employees(status);\nCREATE INDEX idx_employees_department ON bamboohr_employees(department);\nCREATE INDEX idx_employees_synced ON bamboohr_employees(synced_at);\n\nCREATE TABLE bamboohr_sync_log (\n  id            SERIAL PRIMARY KEY,\n  sync_type     TEXT NOT NULL,  -- 'full', 'incremental', 'webhook'\n  started_at    TIMESTAMPTZ NOT NULL,\n  completed_at  TIMESTAMPTZ,\n  employees_created  INTEGER DEFAULT 0,\n  employees_updated  INTEGER DEFAULT 0,\n  employees_deleted  INTEGER DEFAULT 0,\n  errors        JSONB DEFAULT '[]',\n  status        TEXT DEFAULT 'running'  -- 'running', 'completed', 'failed'\n);\n```\n\n### Step 2: Employee Sync Service\n\n```typescript\n\u002F\u002F src\u002Fservices\u002Femployee-sync.ts\nimport { BambooHRClient } from '..\u002Fbamboohr\u002Fclient';\nimport { db } from '..\u002Fdb\u002Fqueries';\n\nconst SYNC_FIELDS = [\n  'firstName', 'lastName', 'displayName', 'workEmail',\n  'jobTitle', 'department', 'division', 'location',\n  'supervisor', 'status', 'hireDate', 'terminationDate',\n  'employeeNumber',\n];\n\nexport class EmployeeSyncService {\n  constructor(private client: BambooHRClient) {}\n\n  async fullSync(): Promise\u003CSyncResult> {\n    const log = await db.createSyncLog('full');\n\n    try {\n      \u002F\u002F One API call for all employee data\n      const report = await this.client.customReport(SYNC_FIELDS);\n      const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };\n\n      for (const emp of report.employees) {\n        try {\n          const existing = await db.getEmployee(parseInt(emp.id));\n          if (existing) {\n            await db.updateEmployee(parseInt(emp.id), emp);\n            result.updated++;\n          } else {\n            await db.createEmployee(parseInt(emp.id), emp);\n            result.created++;\n          }\n        } catch (err) {\n          result.errors.push(`Employee ${emp.id}: ${(err as Error).message}`);\n        }\n      }\n\n      \u002F\u002F Mark employees not in report as inactive\n      const activeIds = new Set(report.employees.map(e => parseInt(e.id)));\n      const localEmployees = await db.getActiveEmployeeIds();\n      for (const localId of localEmployees) {\n        if (!activeIds.has(localId)) {\n          await db.deactivateEmployee(localId);\n          result.deleted++;\n        }\n      }\n\n      await db.completeSyncLog(log.id, result);\n      return result;\n    } catch (err) {\n      await db.failSyncLog(log.id, (err as Error).message);\n      throw err;\n    }\n  }\n\n  async incrementalSync(): Promise\u003CSyncResult> {\n    const lastSync = await db.getLastSyncTimestamp();\n    const changed = await this.client.request\u003Cany>(\n      'GET', `\u002Femployees\u002Fchanged\u002F?since=${lastSync}`,\n    );\n\n    const changedIds = Object.keys(changed.employees || {});\n    if (changedIds.length === 0) return { created: 0, updated: 0, deleted: 0, errors: [] };\n\n    \u002F\u002F Fetch details for changed employees only\n    const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };\n    for (const id of changedIds) {\n      const emp = await this.client.getEmployee(id, SYNC_FIELDS);\n      \u002F\u002F Upsert logic...\n    }\n\n    return result;\n  }\n\n  async handleWebhookEvent(employeeId: string, action: string, fields: Record\u003Cstring, string>) {\n    switch (action) {\n      case 'Created':\n        await db.createEmployee(parseInt(employeeId), fields);\n        break;\n      case 'Updated':\n        await db.updateEmployee(parseInt(employeeId), fields);\n        break;\n      case 'Deleted':\n        await db.deactivateEmployee(parseInt(employeeId));\n        break;\n    }\n  }\n}\n```\n\n### Step 3: Employee Lifecycle Automation\n\n```typescript\n\u002F\u002F src\u002Fservices\u002Flifecycle.ts\nexport class EmployeeLifecycleService {\n  constructor(\n    private bamboohr: BambooHRClient,\n    private slackClient: any,\n    private googleAdmin: any,\n  ) {}\n\n  async onNewEmployee(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { firstName, lastName, workEmail, department, jobTitle, supervisor } = fields;\n\n    \u002F\u002F 1. Create Google Workspace account\n    await this.googleAdmin.createUser({\n      primaryEmail: workEmail,\n      name: { givenName: firstName, familyName: lastName },\n      orgUnitPath: `\u002Fdepartments\u002F${department}`,\n    });\n\n    \u002F\u002F 2. Add to Slack\n    await this.slackClient.inviteUser(workEmail);\n    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);\n\n    \u002F\u002F 3. Notify manager\n    await this.slackClient.sendDM(supervisor, {\n      text: `Your new report ${firstName} ${lastName} (${jobTitle}) starts soon. ` +\n            `BambooHR profile: https:\u002F\u002F${process.env.BAMBOOHR_COMPANY_DOMAIN}.bamboohr.com\u002Femployees\u002Femployee.php?id=${employeeId}`,\n    });\n\n    console.log(`Onboarding complete for ${firstName} ${lastName}`);\n  }\n\n  async onEmployeeTerminated(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { workEmail, firstName, lastName } = fields;\n\n    \u002F\u002F 1. Disable Google Workspace account\n    await this.googleAdmin.suspendUser(workEmail);\n\n    \u002F\u002F 2. Deactivate Slack\n    await this.slackClient.deactivateUser(workEmail);\n\n    \u002F\u002F 3. Archive in downstream systems\n    console.log(`Offboarding complete for ${firstName} ${lastName}`);\n  }\n\n  async onDepartmentChanged(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { workEmail, department } = fields;\n\n    \u002F\u002F Update Google Workspace OU\n    await this.googleAdmin.moveUser(workEmail, `\u002Fdepartments\u002F${department}`);\n\n    \u002F\u002F Update Slack channels\n    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);\n  }\n}\n```\n\n### Step 4: Sync Scheduling\n\n```typescript\n\u002F\u002F src\u002Fjobs\u002Fsync-scheduler.ts\nimport cron from 'node-cron';\n\n\u002F\u002F Full sync: daily at 2 AM\ncron.schedule('0 2 * * *', async () => {\n  console.log('Starting full BambooHR sync...');\n  const result = await syncService.fullSync();\n  console.log(`Full sync: ${result.created} created, ${result.updated} updated, ${result.deleted} deleted`);\n});\n\n\u002F\u002F Incremental sync: every 15 minutes (safety net for missed webhooks)\ncron.schedule('*\u002F15 * * * *', async () => {\n  const result = await syncService.incrementalSync();\n  if (result.created + result.updated + result.deleted > 0) {\n    console.log(`Incremental sync: ${JSON.stringify(result)}`);\n  }\n});\n```\n\n### Step 5: Configuration Management\n\n```typescript\n\u002F\u002F config\u002Fbamboohr.ts\nexport interface BambooHRIntegrationConfig {\n  api: {\n    companyDomain: string;\n    apiKey: string;\n    timeoutMs: number;\n    maxRetries: number;\n  };\n  sync: {\n    fullSyncCron: string;\n    incrementalSyncCron: string;\n    batchSize: number;\n  };\n  cache: {\n    directoryTtlMs: number;\n    employeeTtlMs: number;\n    redisUrl?: string;\n  };\n  webhook: {\n    secret: string;\n    path: string;\n    replayWindowMs: number;\n  };\n}\n\nexport function loadConfig(): BambooHRIntegrationConfig {\n  return {\n    api: {\n      companyDomain: process.env.BAMBOOHR_COMPANY_DOMAIN!,\n      apiKey: process.env.BAMBOOHR_API_KEY!,\n      timeoutMs: 30_000,\n      maxRetries: 3,\n    },\n    sync: {\n      fullSyncCron: '0 2 * * *',\n      incrementalSyncCron: '*\u002F15 * * * *',\n      batchSize: 100,\n    },\n    cache: {\n      directoryTtlMs: 5 * 60 * 1000,\n      employeeTtlMs: 60 * 1000,\n      redisUrl: process.env.REDIS_URL,\n    },\n    webhook: {\n      secret: process.env.BAMBOOHR_WEBHOOK_SECRET!,\n      path: '\u002Fwebhooks\u002Fbamboohr',\n      replayWindowMs: 300_000,\n    },\n  };\n}\n```\n\n## Output\n\n- Layered architecture separating API, service, client, and data concerns\n- Employee sync with full, incremental, and webhook-driven modes\n- PostgreSQL schema for local employee data with audit trail\n- Lifecycle automation (onboarding, offboarding, department changes)\n- Scheduled sync jobs with cron\n- Environment-specific configuration\n\n## Error Handling\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Sync data gaps | Missed webhooks + long incremental interval | Full sync as daily safety net |\n| Duplicate processing | Webhook retry + no idempotency | Idempotency keys in sync log |\n| Stale local data | Cache TTL too long | Webhook-based cache invalidation |\n| Circular dependencies | Poor layer separation | Strict dependency direction (API > Service > Client > DB) |\n\n## Enterprise Considerations\n\n- **Multi-company**: Deploy separate instances per BambooHR company domain, or use tenant-aware client factory\n- **Data residency**: Store employee data in the same region as your BambooHR instance\n- **Compliance**: Implement data retention policies; BambooHR data includes PII\n- **High availability**: Run sync workers as separate pods\u002Fcontainers from API servers\n- **Monitoring**: Alert on sync failures, webhook delivery gaps, and API error spikes\n\n## Resources\n\n- [BambooHR API Documentation](https:\u002F\u002Fdocumentation.bamboohr.com\u002Fdocs)\n- [BambooHR API Reference](https:\u002F\u002Fdocumentation.bamboohr.com\u002Freference)\n- [BambooHR Webhooks](https:\u002F\u002Fdocumentation.bamboohr.com\u002Fdocs\u002Fwebhooks)\n\n## Flagship Skills\n\nFor the complete BambooHR skill pack, start with `bamboohr-install-auth`.\n\n","# BambooHR Reference Architecture\n\n## Overview\n\nProduction-ready architecture for BambooHR integrations covering the three most common patterns: real-time employee sync, HR data pipeline, and employee lifecycle automation.\n\n## Prerequisites\n\n- Understanding of layered architecture and event-driven design\n- BambooHR API knowledge from earlier skills in this pack\n- TypeScript project setup with Node.js 18+\n\n## Instructions\n\n### Architecture Overview\n\n```\n┌──────────────────────────────────────────────────────────┐\n│                    Your Application                       │\n├──────────────┬───────────────┬────────────────────────────┤\n│  API Layer   │  Sync Engine  │  Webhook Handler           │\n│  \u002Fapi\u002F*      │  (Cron\u002FQueue) │  \u002Fwebhooks\u002Fbamboohr        │\n├──────────────┴───────────────┴────────────────────────────┤\n│                   Service Layer                            │\n│  EmployeeService  │  TimeOffService  │  ReportService     │\n├───────────────────┴──────────────────┴────────────────────┤\n│                BambooHR Client Layer                       │\n│  BambooHRClient  │  Cache  │  RetryHandler  │  Metrics    │\n├───────────────────┴─────────┴────────────────┴────────────┤\n│                   Data Layer                               │\n│  PostgreSQL (employees)  │  Redis (cache)  │  S3 (files)  │\n└───────────────────────────────────────────────────────────┘\n                           │\n                           ▼\n              ┌────────────────────────┐\n              │   BambooHR REST API    │\n              │ api.bamboohr.com\u002Fapi\u002F  │\n              │   gateway.php\u002F{co}\u002Fv1  │\n              └────────────────────────┘\n```\n\n### Project Structure\n\n```\nbamboohr-integration\u002F\n├── src\u002F\n│   ├── bamboohr\u002F\n│   │   ├── client.ts              # HTTP client (from sdk-patterns)\n│   │   ├── types.ts               # BambooHR API response types\n│   │   ├── retry.ts               # Retry with Retry-After support\n│   │   ├── cache.ts               # LRU + Redis cache layer\n│   │   └── metrics.ts             # Request counting and latency\n│   ├── services\u002F\n│   │   ├── employee-sync.ts       # Incremental directory sync\n│   │   ├── time-off.ts            # PTO balance and request management\n│   │   ├── reports.ts             # Custom report generation\n│   │   └── lifecycle.ts           # Onboarding\u002Foffboarding automation\n│   ├── handlers\u002F\n│   │   ├── webhook.ts             # Webhook signature verification + routing\n│   │   └── events.ts              # Employee change event processors\n│   ├── api\u002F\n│   │   ├── health.ts              # Health check endpoint\n│   │   ├── employees.ts           # REST API for local employee data\n│   │   └── reports.ts             # Report generation endpoints\n│   ├── jobs\u002F\n│   │   ├── full-sync.ts           # Scheduled full directory sync\n│   │   ├── incremental-sync.ts    # Frequent delta sync\n│   │   └── report-export.ts       # Scheduled report export\n│   └── db\u002F\n│       ├── schema.sql             # PostgreSQL schema\n│       └── queries.ts             # Database queries\n├── tests\u002F\n│   ├── unit\u002F\n│   │   ├── client.test.ts\n│   │   ├── employee-sync.test.ts\n│   │   └── webhook.test.ts\n│   ├── integration\u002F\n│   │   └── bamboohr-live.test.ts\n│   └── mocks\u002F\n│       └── bamboohr-handlers.ts   # MSW handlers\n├── config\u002F\n│   ├── default.json\n│   ├── production.json\n│   └── test.json\n└── docker-compose.yml             # PostgreSQL + Redis for local dev\n```\n\n### Step 1: Data Model\n\n```sql\n-- db\u002Fschema.sql\nCREATE TABLE bamboohr_employees (\n  id               INTEGER PRIMARY KEY,  -- BambooHR employee ID\n  first_name       TEXT NOT NULL,\n  last_name        TEXT NOT NULL,\n  display_name     TEXT,\n  work_email       TEXT,\n  job_title        TEXT,\n  department       TEXT,\n  division         TEXT,\n  location         TEXT,\n  supervisor_id    INTEGER REFERENCES bamboohr_employees(id),\n  status           TEXT DEFAULT 'Active',\n  hire_date        DATE,\n  termination_date DATE,\n  employee_number  TEXT,\n  raw_data         JSONB,                -- Full BambooHR response\n  synced_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n  created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n  updated_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX idx_employees_status ON bamboohr_employees(status);\nCREATE INDEX idx_employees_department ON bamboohr_employees(department);\nCREATE INDEX idx_employees_synced ON bamboohr_employees(synced_at);\n\nCREATE TABLE bamboohr_sync_log (\n  id            SERIAL PRIMARY KEY,\n  sync_type     TEXT NOT NULL,  -- 'full', 'incremental', 'webhook'\n  started_at    TIMESTAMPTZ NOT NULL,\n  completed_at  TIMESTAMPTZ,\n  employees_created  INTEGER DEFAULT 0,\n  employees_updated  INTEGER DEFAULT 0,\n  employees_deleted  INTEGER DEFAULT 0,\n  errors        JSONB DEFAULT '[]',\n  status        TEXT DEFAULT 'running'  -- 'running', 'completed', 'failed'\n);\n```\n\n### Step 2: Employee Sync Service\n\n```typescript\n\u002F\u002F src\u002Fservices\u002Femployee-sync.ts\nimport { BambooHRClient } from '..\u002Fbamboohr\u002Fclient';\nimport { db } from '..\u002Fdb\u002Fqueries';\n\nconst SYNC_FIELDS = [\n  'firstName', 'lastName', 'displayName', 'workEmail',\n  'jobTitle', 'department', 'division', 'location',\n  'supervisor', 'status', 'hireDate', 'terminationDate',\n  'employeeNumber',\n];\n\nexport class EmployeeSyncService {\n  constructor(private client: BambooHRClient) {}\n\n  async fullSync(): Promise\u003CSyncResult> {\n    const log = await db.createSyncLog('full');\n\n    try {\n      \u002F\u002F One API call for all employee data\n      const report = await this.client.customReport(SYNC_FIELDS);\n      const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };\n\n      for (const emp of report.employees) {\n        try {\n          const existing = await db.getEmployee(parseInt(emp.id));\n          if (existing) {\n            await db.updateEmployee(parseInt(emp.id), emp);\n            result.updated++;\n          } else {\n            await db.createEmployee(parseInt(emp.id), emp);\n            result.created++;\n          }\n        } catch (err) {\n          result.errors.push(`Employee ${emp.id}: ${(err as Error).message}`);\n        }\n      }\n\n      \u002F\u002F Mark employees not in report as inactive\n      const activeIds = new Set(report.employees.map(e => parseInt(e.id)));\n      const localEmployees = await db.getActiveEmployeeIds();\n      for (const localId of localEmployees) {\n        if (!activeIds.has(localId)) {\n          await db.deactivateEmployee(localId);\n          result.deleted++;\n        }\n      }\n\n      await db.completeSyncLog(log.id, result);\n      return result;\n    } catch (err) {\n      await db.failSyncLog(log.id, (err as Error).message);\n      throw err;\n    }\n  }\n\n  async incrementalSync(): Promise\u003CSyncResult> {\n    const lastSync = await db.getLastSyncTimestamp();\n    const changed = await this.client.request\u003Cany>(\n      'GET', `\u002Femployees\u002Fchanged\u002F?since=${lastSync}`,\n    );\n\n    const changedIds = Object.keys(changed.employees || {});\n    if (changedIds.length === 0) return { created: 0, updated: 0, deleted: 0, errors: [] };\n\n    \u002F\u002F Fetch details for changed employees only\n    const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };\n    for (const id of changedIds) {\n      const emp = await this.client.getEmployee(id, SYNC_FIELDS);\n      \u002F\u002F Upsert logic...\n    }\n\n    return result;\n  }\n\n  async handleWebhookEvent(employeeId: string, action: string, fields: Record\u003Cstring, string>) {\n    switch (action) {\n      case 'Created':\n        await db.createEmployee(parseInt(employeeId), fields);\n        break;\n      case 'Updated':\n        await db.updateEmployee(parseInt(employeeId), fields);\n        break;\n      case 'Deleted':\n        await db.deactivateEmployee(parseInt(employeeId));\n        break;\n    }\n  }\n}\n```\n\n### Step 3: Employee Lifecycle Automation\n\n```typescript\n\u002F\u002F src\u002Fservices\u002Flifecycle.ts\nexport class EmployeeLifecycleService {\n  constructor(\n    private bamboohr: BambooHRClient,\n    private slackClient: any,\n    private googleAdmin: any,\n  ) {}\n\n  async onNewEmployee(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { firstName, lastName, workEmail, department, jobTitle, supervisor } = fields;\n\n    \u002F\u002F 1. Create Google Workspace account\n    await this.googleAdmin.createUser({\n      primaryEmail: workEmail,\n      name: { givenName: firstName, familyName: lastName },\n      orgUnitPath: `\u002Fdepartments\u002F${department}`,\n    });\n\n    \u002F\u002F 2. Add to Slack\n    await this.slackClient.inviteUser(workEmail);\n    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);\n\n    \u002F\u002F 3. Notify manager\n    await this.slackClient.sendDM(supervisor, {\n      text: `Your new report ${firstName} ${lastName} (${jobTitle}) starts soon. ` +\n            `BambooHR profile: https:\u002F\u002F${process.env.BAMBOOHR_COMPANY_DOMAIN}.bamboohr.com\u002Femployees\u002Femployee.php?id=${employeeId}`,\n    });\n\n    console.log(`Onboarding complete for ${firstName} ${lastName}`);\n  }\n\n  async onEmployeeTerminated(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { workEmail, firstName, lastName } = fields;\n\n    \u002F\u002F 1. Disable Google Workspace account\n    await this.googleAdmin.suspendUser(workEmail);\n\n    \u002F\u002F 2. Deactivate Slack\n    await this.slackClient.deactivateUser(workEmail);\n\n    \u002F\u002F 3. Archive in downstream systems\n    console.log(`Offboarding complete for ${firstName} ${lastName}`);\n  }\n\n  async onDepartmentChanged(employeeId: string, fields: Record\u003Cstring, string>) {\n    const { workEmail, department } = fields;\n\n    \u002F\u002F Update Google Workspace OU\n    await this.googleAdmin.moveUser(workEmail, `\u002Fdepartments\u002F${department}`);\n\n    \u002F\u002F Update Slack channels\n    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);\n  }\n}\n```\n\n### Step 4: Sync Scheduling\n\n```typescript\n\u002F\u002F src\u002Fjobs\u002Fsync-scheduler.ts\nimport cron from 'node-cron';\n\n\u002F\u002F Full sync: daily at 2 AM\ncron.schedule('0 2 * * *', async () => {\n  console.log('Starting full BambooHR sync...');\n  const result = await syncService.fullSync();\n  console.log(`Full sync: ${result.created} created, ${result.updated} updated, ${result.deleted} deleted`);\n});\n\n\u002F\u002F Incremental sync: every 15 minutes (safety net for missed webhooks)\ncron.schedule('*\u002F15 * * * *', async () => {\n  const result = await syncService.incrementalSync();\n  if (result.created + result.updated + result.deleted > 0) {\n    console.log(`Incremental sync: ${JSON.stringify(result)}`);\n  }\n});\n```\n\n### Step 5: Configuration Management\n\n```typescript\n\u002F\u002F config\u002Fbamboohr.ts\nexport interface BambooHRIntegrationConfig {\n  api: {\n    companyDomain: string;\n    apiKey: string;\n    timeoutMs: number;\n    maxRetries: number;\n  };\n  sync: {\n    fullSyncCron: string;\n    incrementalSyncCron: string;\n    batchSize: number;\n  };\n  cache: {\n    directoryTtlMs: number;\n    employeeTtlMs: number;\n    redisUrl?: string;\n  };\n  webhook: {\n    secret: string;\n    path: string;\n    replayWindowMs: number;\n  };\n}\n\nexport function loadConfig(): BambooHRIntegrationConfig {\n  return {\n    api: {\n      companyDomain: process.env.BAMBOOHR_COMPANY_DOMAIN!,\n      apiKey: process.env.BAMBOOHR_API_KEY!,\n      timeoutMs: 30_000,\n      maxRetries: 3,\n    },\n    sync: {\n      fullSyncCron: '0 2 * * *',\n      incrementalSyncCron: '*\u002F15 * * * *',\n      batchSize: 100,\n    },\n    cache: {\n      directoryTtlMs: 5 * 60 * 1000,\n      employeeTtlMs: 60 * 1000,\n      redisUrl: process.env.REDIS_URL,\n    },\n    webhook: {\n      secret: process.env.BAMBOOHR_WEBHOOK_SECRET!,\n      path: '\u002Fwebhooks\u002Fbamboohr',\n      replayWindowMs: 300_000,\n    },\n  };\n}\n```\n\n## Output\n\n- Layered architecture separating API, service, client, and data concerns\n- Employee sync with full, incremental, and webhook-driven modes\n- PostgreSQL schema for local employee data with audit trail\n- Lifecycle automation (onboarding, offboarding, department changes)\n- Scheduled sync jobs with cron\n- Environment-specific configuration\n\n## Error Handling\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Sync data gaps | Missed webhooks + long incremental interval | Full sync as daily safety net |\n| Duplicate processing | Webhook retry + no idempotency | Idempotency keys in sync log |\n| Stale local data | Cache TTL too long | Webhook-based cache invalidation |\n| Circular dependencies | Poor layer separation | Strict dependency direction (API > Service > Client > DB) |\n\n## Enterprise Considerations\n\n- **Multi-company**: Deploy separate instances per BambooHR company domain, or use tenant-aware client factory\n- **Data residency**: Store employee data in the same region as your BambooHR instance\n- **Compliance**: Implement data retention policies; BambooHR data includes PII\n- **High availability**: Run sync workers as separate pods\u002Fcontainers from API servers\n- **Monitoring**: Alert on sync failures, webhook delivery gaps, and API error spikes\n\n## Resources\n\n- [BambooHR API Documentation](https:\u002F\u002Fdocumentation.bamboohr.com\u002Fdocs)\n- [BambooHR API Reference](https:\u002F\u002Fdocumentation.bamboohr.com\u002Freference)\n- [BambooHR Webhooks](https:\u002F\u002Fdocumentation.bamboohr.com\u002Fdocs\u002Fwebhooks)\n\n## Flagship Skills\n\nFor the complete BambooHR skill pack, start with `bamboohr-install-auth`.\n\n",{"name":5,"tags":49,"author":51,"license":15,"version":52,"description":53,"allowed-tools":54,"compatible-with":55},[50,29,25,26],"saas","Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Implement BambooHR reference architecture for production HR data pipelines.\nUse when designing new BambooHR integrations, building employee sync systems,\nor establishing architecture standards for BambooHR-powered applications.\nTrigger with phrases like \"bamboohr architecture\", \"bamboohr design\",\n\"bamboohr project structure\", \"bamboohr system design\", \"bamboohr pipeline\".\n","Read, Grep","claude-code","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbamboohr-reference-architecture",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":66,"prompt":76,"zipAvailable":77},[67,70,73],{"tool":68,"command":69},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill bamboohr-reference-architecture",{"tool":71,"command":72},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill bamboohr-reference-architecture",{"tool":74,"command":75},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill bamboohr-reference-architecture","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbamboohr-reference-architecture\u002FSKILL.md",true,[],[80,94,104,113,119,126],{"skillNo":81,"name":82,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":90,"publishedAt":44},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[85],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[91,92,93],"model","model hierarchy","model hierarchy skill",{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":100,"publishedAt":44},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[99],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},[101,102,103],"MCP","zoom","zoom mcp",{"skillNo":105,"name":106,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":109,"publishedAt":44},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[110,111,112,102],"sdk","zoom apps sdk","apps",{"skillNo":114,"name":115,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":118,"publishedAt":44},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[115],{"skillNo":120,"name":121,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":125,"publishedAt":44},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[124],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},[121],{"skillNo":127,"name":128,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":131,"publishedAt":44},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[128]]