Skip to main content
CMDashCybersecurity Maturity Dashboard
← Back to BambooHR

BambooHR — Integration Setup

Connecting BambooHR gives CMDash your employee roster: who works here, when they joined, when they left, and who reports to whom. That roster is the evidence behind joiner/leaver controls, periodic access reviews, and the contractor population auditors ask about separately.

Prerequisites

  • A BambooHR account with API access
  • Ability to generate an API key (any user can generate one; the key inherits that user's permissions)
  • CMDash Essentials tier or above

Step 1: Create an API Key

  1. Log in to BambooHR.
  2. Click your avatar (top right) → API Keys.
  3. Click Add New Key, name it CMDash Integration, and Generate Key.
  4. Copy the key immediately — it is shown only once.

Use a least-privileged service user. A BambooHR API key carries the permissions of the user who created it, and a full-admin user's key can read compensation, Social Security numbers, and home addresses. CMDash never requests any of those fields (see What CMDash Deliberately Does Not Read), but the safest configuration is a dedicated BambooHR user in a custom access level that can see only: Employee Number, Name, Work Email, Job Title, Department, Division, Hire Date, Termination Date, Employment Status, and Reporting To.

Step 2: Connect in CMDash

  1. Navigate to Integrations in your CMDash dashboard.
  2. Find BambooHR and click Connect.
  3. Enter:
    • Company Domain — the acme in acme.bamboohr.com
    • API Key (stored as this connection's access token; sent as HTTP Basic auth with the key as the username, per BambooHR's documented scheme)
  4. Click Save & Test Connection.

What Syncs

Data TypeBambooHR APISync Frequency
Workforce rosterGET https://<companyDomain>.bamboohr.com/api/v1/employees (cursor-paginated)Every 4 hours by default (configurable per connection)

Each roster row is normalized to: external id, work email, display name, employment status, employment type (employee vs contractor), hire date, termination date, department, job title, and a manager reference. CMDash also derives a summary alongside the rows — headcount, active/terminated counts, contractor count, joiners and leavers in the last 30 days, and terminatedButActiveCount: people with a termination date in the past whom BambooHR still reports as Active.

That last number is the one to build a continuous-compliance rule on. See Turning the roster into a control.

Status mapping

BambooHR's status field is only ever Active or Inactive, which does not distinguish a termination from a leave of absence. CMDash maps:

BambooHRCMDashWhy
ActiveactiveEven with a past termination date — that mismatch is the finding, not an error to smooth over
Inactive + a termination dateterminated
Inactive, no termination dateunknownGuessing "terminated" would silently clear an offboarding finding for someone who is merely on leave

Employment type comes from employmentHistoryStatus. Values containing "contract", "freelance", or "temp" map to contractor; "full-time", "part-time", and "intern" map to employee; anything else stays unknown rather than being rounded to employee.

What CMDash Deliberately Does Not Read

CMDash sends BambooHR an explicit fields allowlist on every request, so the following are never transmitted at all:

  • Compensation — payRate, payType, payPer, commissionRate, bonusAmount
  • National identifiers — ssn, nin, nationalId, tax file numbers
  • Demographics — dateOfBirth, gender, maritalStatus, ethnicity, veteran and disability status
  • Home/personal contact — address1/address2/city/zipcode, homeEmail, homePhone, mobilePhone
  • Performance, benefits, and dependants endpoints — never called

None of it is evidence for any control CMDash tracks, and holding it would turn a CMDash breach into a payroll and PII breach. If BambooHR returns an un-requested field anyway (a custom field, or a future API change), CMDash discards it during normalization rather than storing it — the roster record is constructed field by field from a fixed list, never copied wholesale.

Turning the roster into a control

Create a continuous-compliance rule against the roster:

  • Provider: BambooHR
  • Data type: Workforce
  • Condition: terminatedButActiveCount <= 0
  • Target status: Implemented

The control reads as Implemented while every departed employee is correctly marked inactive in BambooHR, and drifts to At Risk / Failing when one is not — with an auto-opened remediation task. Other useful fields on the same datum: headcount, activeCount, terminatedCount, contractorCount, joinersLast30d, leaversLast30d.

Training completion

BambooHR's Training module is not synced. Its training records are addressable only one employee at a time (/v1/training/record/employee/{id}), which would mean one request per person on every sync; reading only a subset instead would produce a completion percentage over a biased sample, which is worse than no number. Security-awareness training completion in CMDash comes from KnowBe4.

Troubleshooting

  • 401 Unauthorized: The API key was revoked, or the user who created it was deactivated. Note that CMDash sends the key as the Basic-auth username — a key pasted into a "bearer token" field elsewhere will not work here.
  • 403 Forbidden: The key's owning user cannot see the requested fields. Grant the least-privileged field set listed in Step 1.
  • Roster is missing people: BambooHR's employees collection excludes some record types depending on your configuration. Compare headcount in CMDash against BambooHR's own employee list.
  • Sync fails with "refusing to store a partial roster": Your roster exceeds 50,000 rows. CMDash fails rather than truncating, because a missing leaver reads as "no offboarding failure". Contact support.

Rate Limits & Sync Behavior

SettingValue
Sync frequencyEvery 4 hours by default (syncInterval is configurable per connection)
Page size250 employees per request, walked by cursor until BambooHR stops issuing one
Roster cap50,000 rows — exceeding it fails the sync rather than storing a partial roster
Rate limit handlingRequests are paced through CMDash's shared per-tenant token bucket
Circuit breakerRepeated failures open the breaker and skip the cycle rather than hammering BambooHR
Failed sync retryJob queue retries 3 times with exponential backoff (starting at 30s) — see docs/developers/dead-letter.md

CMDash follows BambooHR's pre-built _links.next pagination URL only when it is same-origin with your configured company domain; a link pointing anywhere else is ignored and CMDash builds the next URL from the cursor itself, so your API key is never sent off-origin.

Credential Rotation

BambooHR API keys do not expire. Rotate quarterly.

  1. In BambooHR: avatar → API Keys → Add New Key.
  2. In CMDash: Settings → Integrations → BambooHR → Edit, enter the new key, Save.
  3. Delete the old key in BambooHR.

Ready to see your posture?

Start a 30-day free trial of any paid plan — a card is required, and it converts unless you cancel — or sign up for Free with no card at all.