BambooHR Core Workflow A — Employee Management & Reports
Overview
Primary BambooHR workflows: CRUD operations on employees, directory sync to external systems, custom reports, and table data (job history, compensation, emergency contacts).
Prerequisites
- Completed
bamboohr-install-auth setup
BambooHRClient from bamboohr-sdk-patterns
- API key with appropriate permissions (read or read+write)
Instructions
Step 1: Add a New Employee
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',
}),
});
const locationHeader = newEmpRes.headers.get('Location');
const newId = locationHeader?.split('/').pop();
console.log(`Created employee ID: ${newId}`);
Step 2: Update Employee Fields
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.
Step 3: Directory Sync to External System
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: [] };
const { employees } = await client.getDirectory();
const changedRes = await client.request<Record<string, { lastChanged: string }>>(
'GET', `/employees/changed/?since=${lastSyncTimestamp}`,
);
for (const [empId, meta] of Object.entries(changedRes. || {})) {
{
emp = client.(empId, [
, , , ,
, , , ,
]);
action = emp. ? : emp. === ? : ;
(emp , action);
result[action keyof ]++;
} (err) {
result..();
}
}
result;
}
Step 4: Custom Reports
const headcountReport = await client.customReport(
['department', 'division', 'jobTitle', 'hireDate', 'status', 'location'],
{ lastChanged: { includeNull: 'no', value: '2025-01-01T00:00:00Z' } },
);
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}`);
}
Step 5: Saved Reports
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);
}
Step 6: Employee Table Data
BambooHR stores structured data in "tables" — each employee has rows in tables like jobInfo, employmentStatus, compensation, emergencyContacts.
const jobHistory = await client.getTableRows(123, 'jobInfo');
const compensation = await client.getTableRows(123, 'compensation');
const emergencyContacts = await client.getTableRows(123, 'emergencyContacts');
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 |
Output
- New employees created with auto-assigned IDs
- Employee fields updated with position history tracking
- Directory sync with incremental change detection
- Custom and saved reports with aggregation
- Table data CRUD for job history, compensation, contacts
Error Handling
| 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 |
Resources
Next Steps
For time off and benefits workflows, see bamboohr-core-workflow-b.