Skip to content
OpenSmartRoute
Skillv1.0.0

bamboohr-reference-architecture

Implement BambooHR reference architecture for production HR data pipelines. Use when designing new BambooHR integrations, building employee sync systems, or establishing architecture standards for Bam

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (plugins/saas-packs/bamboohr-pack/skills/bamboohr-reference-architecture/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill bamboohr-reference-architecture. Copyright stays with the author (MIT).

BambooHR Reference Architecture

Overview

Production-ready architecture for BambooHR integrations covering the three most common patterns: real-time employee sync, HR data pipeline, and employee lifecycle automation.

Prerequisites

  • Understanding of layered architecture and event-driven design
  • BambooHR API knowledge from earlier skills in this pack
  • TypeScript project setup with Node.js 18+

Instructions

Architecture Overview

┌──────────────────────────────────────────────────────────┐
│                    Your Application                       │
├──────────────┬───────────────┬────────────────────────────┤
│  API Layer   │  Sync Engine  │  Webhook Handler           │
│  /api/*      │  (Cron/Queue) │  /webhooks/bamboohr        │
├──────────────┴───────────────┴────────────────────────────┤
│                   Service Layer                            │
│  EmployeeService  │  TimeOffService  │  ReportService     │
├───────────────────┴──────────────────┴────────────────────┤
│                BambooHR Client Layer                       │
│  BambooHRClient  │  Cache  │  RetryHandler  │  Metrics    │
├───────────────────┴─────────┴────────────────┴────────────┤
│                   Data Layer                               │
│  PostgreSQL (employees)  │  Redis (cache)  │  S3 (files)  │
└───────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │   BambooHR REST API    │
              │ api.bamboohr.com/api/  │
              │   gateway.php/{co}/v1  │
              └────────────────────────┘

Project Structure

bamboohr-integration/
├── src/
│   ├── bamboohr/
│   │   ├── client.ts              # HTTP client (from sdk-patterns)
│   │   ├── types.ts               # BambooHR API response types
│   │   ├── retry.ts               # Retry with Retry-After support
│   │   ├── cache.ts               # LRU + Redis cache layer
│   │   └── metrics.ts             # Request counting and latency
│   ├── services/
│   │   ├── employee-sync.ts       # Incremental directory sync
│   │   ├── time-off.ts            # PTO balance and request management
│   │   ├── reports.ts             # Custom report generation
│   │   └── lifecycle.ts           # Onboarding/offboarding automation
│   ├── handlers/
│   │   ├── webhook.ts             # Webhook signature verification + routing
│   │   └── events.ts              # Employee change event processors
│   ├── api/
│   │   ├── health.ts              # Health check endpoint
│   │   ├── employees.ts           # REST API for local employee data
│   │   └── reports.ts             # Report generation endpoints
│   ├── jobs/
│   │   ├── full-sync.ts           # Scheduled full directory sync
│   │   ├── incremental-sync.ts    # Frequent delta sync
│   │   └── report-export.ts       # Scheduled report export
│   └── db/
│       ├── schema.sql             # PostgreSQL schema
│       └── queries.ts             # Database queries
├── tests/
│   ├── unit/
│   │   ├── client.test.ts
│   │   ├── employee-sync.test.ts
│   │   └── webhook.test.ts
│   ├── integration/
│   │   └── bamboohr-live.test.ts
│   └── mocks/
│       └── bamboohr-handlers.ts   # MSW handlers
├── config/
│   ├── default.json
│   ├── production.json
│   └── test.json
└── docker-compose.yml             # PostgreSQL + Redis for local dev

Step 1: Data Model

-- db/schema.sql
CREATE TABLE bamboohr_employees (
  id               INTEGER PRIMARY KEY,  -- BambooHR employee ID
  first_name       TEXT NOT NULL,
  last_name        TEXT NOT NULL,
  display_name     TEXT,
  work_email       TEXT,
  job_title        TEXT,
  department       TEXT,
  division         TEXT,
  location         TEXT,
  supervisor_id    INTEGER REFERENCES bamboohr_employees(id),
  status           TEXT DEFAULT 'Active',
  hire_date        DATE,
  termination_date DATE,
  employee_number  TEXT,
  raw_data         JSONB,                -- Full BambooHR response
  synced_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_employees_status ON bamboohr_employees(status);
CREATE INDEX idx_employees_department ON bamboohr_employees(department);
CREATE INDEX idx_employees_synced ON bamboohr_employees(synced_at);

CREATE TABLE bamboohr_sync_log (
  id            SERIAL PRIMARY KEY,
  sync_type     TEXT NOT NULL,  -- 'full', 'incremental', 'webhook'
  started_at    TIMESTAMPTZ NOT NULL,
  completed_at  TIMESTAMPTZ,
  employees_created  INTEGER DEFAULT 0,
  employees_updated  INTEGER DEFAULT 0,
  employees_deleted  INTEGER DEFAULT 0,
  errors        JSONB DEFAULT '[]',
  status        TEXT DEFAULT 'running'  -- 'running', 'completed', 'failed'
);

Step 2: Employee Sync Service

// src/services/employee-sync.ts
import { BambooHRClient } from '../bamboohr/client';
import { db } from '../db/queries';

const SYNC_FIELDS = [
  'firstName', 'lastName', 'displayName', 'workEmail',
  'jobTitle', 'department', 'division', 'location',
  'supervisor', 'status', 'hireDate', 'terminationDate',
  'employeeNumber',
];

export class EmployeeSyncService {
  constructor(private client: BambooHRClient) {}

  async fullSync(): Promise<SyncResult> {
    const log = await db.createSyncLog('full');

    try {
      // One API call for all employee data
      const report = await this.client.customReport(SYNC_FIELDS);
      const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };

      for (const emp of report.employees) {
        try {
          const existing = await db.getEmployee(parseInt(emp.id));
          if (existing) {
            await db.updateEmployee(parseInt(emp.id), emp);
            result.updated++;
          } else {
            await db.createEmployee(parseInt(emp.id), emp);
            result.created++;
          }
        } catch (err) {
          result.errors.push(`Employee ${emp.id}: ${(err as Error).message}`);
        }
      }

      // Mark employees not in report as inactive
      const activeIds = new Set(report.employees.map(e => parseInt(e.id)));
      const localEmployees = await db.getActiveEmployeeIds();
      for (const localId of localEmployees) {
        if (!activeIds.has(localId)) {
          await db.deactivateEmployee(localId);
          result.deleted++;
        }
      }

      await db.completeSyncLog(log.id, result);
      return result;
    } catch (err) {
      await db.failSyncLog(log.id, (err as Error).message);
      throw err;
    }
  }

  async incrementalSync(): Promise<SyncResult> {
    const lastSync = await db.getLastSyncTimestamp();
    const changed = await this.client.request<any>(
      'GET', `/employees/changed/?since=${lastSync}`,
    );

    const changedIds = Object.keys(changed.employees || {});
    if (changedIds.length === 0) return { created: 0, updated: 0, deleted: 0, errors: [] };

    // Fetch details for changed employees only
    const result = { created: 0, updated: 0, deleted: 0, errors: [] as string[] };
    for (const id of changedIds) {
      const emp = await this.client.getEmployee(id, SYNC_FIELDS);
      // Upsert logic...
    }

    return result;
  }

  async handleWebhookEvent(employeeId: string, action: string, fields: Record<string, string>) {
    switch (action) {
      case 'Created':
        await db.createEmployee(parseInt(employeeId), fields);
        break;
      case 'Updated':
        await db.updateEmployee(parseInt(employeeId), fields);
        break;
      case 'Deleted':
        await db.deactivateEmployee(parseInt(employeeId));
        break;
    }
  }
}

Step 3: Employee Lifecycle Automation

// src/services/lifecycle.ts
export class EmployeeLifecycleService {
  constructor(
    private bamboohr: BambooHRClient,
    private slackClient: any,
    private googleAdmin: any,
  ) {}

  async onNewEmployee(employeeId: string, fields: Record<string, string>) {
    const { firstName, lastName, workEmail, department, jobTitle, supervisor } = fields;

    // 1. Create Google Workspace account
    await this.googleAdmin.createUser({
      primaryEmail: workEmail,
      name: { givenName: firstName, familyName: lastName },
      orgUnitPath: `/departments/${department}`,
    });

    // 2. Add to Slack
    await this.slackClient.inviteUser(workEmail);
    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);

    // 3. Notify manager
    await this.slackClient.sendDM(supervisor, {
      text: `Your new report ${firstName} ${lastName} (${jobTitle}) starts soon. ` +
            `BambooHR profile: https://${process.env.BAMBOOHR_COMPANY_DOMAIN}.bamboohr.com/employees/employee.php?id=${employeeId}`,
    });

    console.log(`Onboarding complete for ${firstName} ${lastName}`);
  }

  async onEmployeeTerminated(employeeId: string, fields: Record<string, string>) {
    const { workEmail, firstName, lastName } = fields;

    // 1. Disable Google Workspace account
    await this.googleAdmin.suspendUser(workEmail);

    // 2. Deactivate Slack
    await this.slackClient.deactivateUser(workEmail);

    // 3. Archive in downstream systems
    console.log(`Offboarding complete for ${firstName} ${lastName}`);
  }

  async onDepartmentChanged(employeeId: string, fields: Record<string, string>) {
    const { workEmail, department } = fields;

    // Update Google Workspace OU
    await this.googleAdmin.moveUser(workEmail, `/departments/${department}`);

    // Update Slack channels
    await this.slackClient.addToChannel(workEmail, `#${department.toLowerCase()}`);
  }
}

Step 4: Sync Scheduling

// src/jobs/sync-scheduler.ts
import cron from 'node-cron';

// Full sync: daily at 2 AM
cron.schedule('0 2 * * *', async () => {
  console.log('Starting full BambooHR sync...');
  const result = await syncService.fullSync();
  console.log(`Full sync: ${result.created} created, ${result.updated} updated, ${result.deleted} deleted`);
});

// Incremental sync: every 15 minutes (safety net for missed webhooks)
cron.schedule('*/15 * * * *', async () => {
  const result = await syncService.incrementalSync();
  if (result.created + result.updated + result.deleted > 0) {
    console.log(`Incremental sync: ${JSON.stringify(result)}`);
  }
});

Step 5: Configuration Management

// config/bamboohr.ts
export interface BambooHRIntegrationConfig {
  api: {
    companyDomain: string;
    apiKey: string;
    timeoutMs: number;
    maxRetries: number;
  };
  sync: {
    fullSyncCron: string;
    incrementalSyncCron: string;
    batchSize: number;
  };
  cache: {
    directoryTtlMs: number;
    employeeTtlMs: number;
    redisUrl?: string;
  };
  webhook: {
    secret: string;
    path: string;
    replayWindowMs: number;
  };
}

export function loadConfig(): BambooHRIntegrationConfig {
  return {
    api: {
      companyDomain: process.env.BAMBOOHR_COMPANY_DOMAIN!,
      apiKey: process.env.BAMBOOHR_API_KEY!,
      timeoutMs: 30_000,
      maxRetries: 3,
    },
    sync: {
      fullSyncCron: '0 2 * * *',
      incrementalSyncCron: '*/15 * * * *',
      batchSize: 100,
    },
    cache: {
      directoryTtlMs: 5 * 60 * 1000,
      employeeTtlMs: 60 * 1000,
      redisUrl: process.env.REDIS_URL,
    },
    webhook: {
      secret: process.env.BAMBOOHR_WEBHOOK_SECRET!,
      path: '/webhooks/bamboohr',
      replayWindowMs: 300_000,
    },
  };
}

Output

  • Layered architecture separating API, service, client, and data concerns
  • Employee sync with full, incremental, and webhook-driven modes
  • PostgreSQL schema for local employee data with audit trail
  • Lifecycle automation (onboarding, offboarding, department changes)
  • Scheduled sync jobs with cron
  • Environment-specific configuration

Examples

Use an API boundary that authenticates the caller, checks a policy-managed employee/field scope, and creates an idempotency receipt before delegating to a BambooHR adapter. Keep event queues, caches, and audit stores encrypted and access-controlled; workers must recheck authorization before side effects and pause on webhook or reconciliation uncertainty.

Error Handling

Issue Cause Solution
Sync data gaps Missed webhooks + long incremental interval Full sync as daily safety net
Duplicate processing Webhook retry + no idempotency Idempotency keys in sync log
Stale local data Cache TTL too long Webhook-based cache invalidation
Circular dependencies Poor layer separation Strict dependency direction (API > Service > Client > DB)

Enterprise Considerations

  • Multi-company: Deploy separate instances per BambooHR company domain, or use tenant-aware client factory
  • Data residency: Store employee data in the same region as your BambooHR instance
  • Compliance: Implement data retention policies; BambooHR data includes PII
  • High availability: Run sync workers as separate pods/containers from API servers
  • Monitoring: Alert on sync failures, webhook delivery gaps, and API error spikes

Resources

Flagship Skills

For the complete BambooHR skill pack, start with bamboohr-install-auth.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-bamboohr-refe-e0e6a0/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-bamboohr-refe-e0e6a0.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-bamboohr-refe-e0e6a0",
  "kind": "skill",
  "name": "bamboohr-reference-architecture",
  "description": "Implement BambooHR reference architecture for production HR data pipelines. Use when designing new BambooHR integrations, building employee sync systems, or establishing architecture standards for BambooHR-powered applications. Trigger with phrases like \"bamboohr architecture\", \"bamboohr design\", \"bamboohr project structure\", \"bamboohr system design\", \"bamboohr pipeline\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "saas",
      "hr",
      "bamboohr",
      "architecture",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Implement BambooHR reference architecture for production HR data pipelines. Use when designing new BambooHR integrations, building employee sync systems, or establishing architecture standards for BambooHR-powered applications. Trigger with phrases like \"bamboohr architecture\", \"bamboohr design\", \"bamboohr project structure\", \"bamboohr system design\", \"bamboohr pipeline\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "plugins/saas-packs/bamboohr-pack/skills/bamboohr-reference-architecture/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/plugins/saas-packs/bamboohr-pack/skills/bamboohr-reference-architecture/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/plugins/saas-packs/bamboohr-pack/skills/bamboohr-reference-architecture/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Grep"
    ],
    "license": "MIT"
  },
  "instructions": "# 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├──────────────┬────",
  "cost": {
    "context_tokens": 3380
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-bamboohr-refe-e0e6a0/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.