From bamboohr-pack
Execute BambooHR primary workflows: employee CRUD, directory sync, and custom reports. Use when managing employees, syncing employee data to external systems, or building HR data pipelines with BambooHR. Trigger with phrases like "bamboohr employees", "bamboohr employee management", "sync bamboohr directory", "bamboohr custom report", "add employee bamboohr".
npx claudepluginhub flight505/skill-forge --plugin bamboohr-packThis skill is limited to using the following tools:
Primary BambooHR workflows: CRUD operations on employees, directory sync to external systems, custom reports, and table data (job history, compensation, emergency contacts).
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Primary BambooHR workflows: CRUD operations on employees, directory sync to external systems, custom reports, and table data (job history, compensation, emergency contacts).
bamboohr-install-auth setupBambooHRClient from bamboohr-sdk-patterns// POST /employees/ — minimum: firstName + lastName
const newEmpRes = await fetch(`${BASE}/employees/`, {
method: 'POST',
headers: { Authorization: AUTH, 'Content-Type': 'application/json' },
body: JSON.stringify({
firstName: 'Sarah',
lastName: 'Chen',
department: 'Engineering',
jobTitle: 'Backend Engineer',
workEmail: 'sarah.chen@acmecorp.com',
hireDate: '2026-04-01',
location: 'San Francisco',
status: 'Active',
}),
});
// New employee ID is in the Location header
const locationHeader = newEmpRes.headers.get('Location');
// e.g., "https://api.bamboohr.com/.../v1/employees/456"
const newId = locationHeader?.split('/').pop();
console.log(`Created employee ID: ${newId}`);
// POST /employees/{id}/ — only send fields you want to change
await fetch(`${BASE}/employees/${newId}/`, {
method: 'POST',
headers: { Authorization: AUTH, 'Content-Type': 'application/json' },
body: JSON.stringify({
jobTitle: 'Senior Backend Engineer',
department: 'Platform Engineering',
}),
});
Fields that trigger position history changes: jobTitle, department, division, location, reportsTo. Updating these creates a new row in the employee's position history table.
interface SyncResult {
created: number;
updated: number;
deactivated: number;
errors: string[];
}
async function syncBambooHRDirectory(
onSync: (emp: BambooEmployee, action: string) => Promise<void>,
): Promise<SyncResult> {
const result: SyncResult = { created: 0, updated: 0, deactivated: 0, errors: [] };
// Fetch full directory
const { employees } = await client.getDirectory();
// Use the "changed since" endpoint for incremental sync
// GET /employees/changed/?since=2026-03-20T00:00:00Z
const changedRes = await client.request<Record<string, { lastChanged: string }>>(
'GET', `/employees/changed/?since=${lastSyncTimestamp}`,
);
for (const [empId, meta] of Object.entries(changedRes.employees || {})) {
try {
const emp = await client.getEmployee(empId, [
'firstName', 'lastName', 'workEmail', 'department',
'jobTitle', 'status', 'hireDate', 'terminationDate',
]);
const action = emp.terminationDate ? 'deactivated' : emp.status === 'Active' ? 'updated' : 'created';
await onSync(emp as any, action);
result[action as keyof SyncResult]++;
} catch (err) {
result.errors.push(`Employee ${empId}: ${(err as Error).message}`);
}
}
return result;
}
// POST /reports/custom?format=JSON — pull arbitrary field combinations
const headcountReport = await client.customReport(
['department', 'division', 'jobTitle', 'hireDate', 'status', 'location'],
{ lastChanged: { includeNull: 'no', value: '2025-01-01T00:00:00Z' } },
);
// Aggregate by department
const deptCounts = new Map<string, number>();
for (const emp of headcountReport.employees) {
const dept = emp.department || 'Unassigned';
deptCounts.set(dept, (deptCounts.get(dept) || 0) + 1);
}
console.log('Headcount by Department:');
for (const [dept, count] of [...deptCounts.entries()].sort((a, b) => b[1] - a[1])) {
console.log(` ${dept}: ${count}`);
}
// GET /reports/{reportId}?format=JSON — run a saved report from BambooHR
const savedReport = await client.request<{
title: string;
employees: Record<string, string>[];
}>('GET', '/reports/42?format=JSON');
console.log(`Report: ${savedReport.title}`);
for (const row of savedReport.employees) {
console.log(row);
}
BambooHR stores structured data in "tables" — each employee has rows in tables like jobInfo, employmentStatus, compensation, emergencyContacts.
// GET /employees/{id}/tables/{tableName} — read table rows
const jobHistory = await client.getTableRows(123, 'jobInfo');
// Returns array: [{ date, jobTitle, department, division, location, reportsTo }, ...]
const compensation = await client.getTableRows(123, 'compensation');
// Returns: [{ startDate, rate, type, reason, comment }, ...]
const emergencyContacts = await client.getTableRows(123, 'emergencyContacts');
// Returns: [{ name, relationship, phone, email }, ...]
// POST /employees/{id}/tables/{tableName} — add a new row
await client.addTableRow(123, 'emergencyContacts', {
name: 'John Smith',
relationship: 'Spouse',
phone: '555-0100',
email: 'john@example.com',
});
Available table names:
| Table | Description |
|---|---|
jobInfo | Job title, department, division, location changes |
employmentStatus | Hire date, termination date, status changes |
compensation | Pay rate, pay type, pay schedule changes |
emergencyContacts | Emergency contact records |
dependents | Employee dependents |
customTable_* | Custom tables created in BambooHR admin |
| Error | Cause | Solution |
|---|---|---|
| 400 on employee create | Missing firstName or lastName | Both are required |
| 403 on compensation tables | API key lacks access | Need admin-level API key |
| 409 on duplicate | Same employeeNumber exists | Use unique employee numbers |
Empty changed response | No changes since timestamp | Normal — nothing to sync |
For time off and benefits workflows, see bamboohr-core-workflow-b.