Sprooster Docs
Connect your directory

BambooHR

Generate an API key in BambooHR and paste it into Sprooster with your subdomain.

Sprooster reads your people from BambooHR: names, titles, departments, reporting lines, locations, hire dates, and birthdays where they're recorded. It's the shortest setup of any connection here, because BambooHR authenticates with a plain API key rather than an app registration.

You'll need to be a BambooHR user whose account can generate API keys, typically an HR administrator or a full admin. Budget about two minutes.

1. Generate an API key

  1. Sign in to BambooHR and click your avatar in the top-right.
  2. Choose API Keys.
  3. Click Add New Key, name it Sprooster, and click Generate Key.
  4. Copy the key. BambooHR shows it once.

The key inherits the permissions of the account that created it, so generate it from an account that can see everyone you want in the chart. A user whose BambooHR role only sees their own department will produce a directory that only contains that department.

2. Find your subdomain

It's the first part of your own BambooHR web address. If you sign in at https://acme.bamboohr.com, your subdomain is acme.

3. Connect

Go to Sprooster's Connect directory step, or Settings → Data sources → BambooHR. Paste the subdomain and the API key. Hit Test connection: it reports a count of your people and a few sample names. Then hit Save connection.

Sprooster syncs once a day, and Sync now in the workspace pulls on demand.

For your security review

The details an IT or security team usually wants before a key is issued:

Auth flow. HTTP Basic against the BambooHR REST gateway, with the API key as the username. No OAuth, no redirect URIs, no user sign-in, no tokens to refresh.

API surface. Three endpoints, all reads:

CallPurpose
GET /v1/employees/directory?onlyCurrent=falseConnection test (counts people, fetches 5 names)
GET /v1/employees/changed?since=…The sync's delta: ids and what happened to them, against a stored cursor
GET /v1/employees/{id}The full record for each changed employee

The first sync passes the epoch as since, which lists everyone; every run after that passes the cursor BambooHR handed back, so only what actually changed is transferred.

Exactly what's read. A fixed field list. Nothing outside it is requested:

displayName, firstName, lastName, jobTitle, department, workEmail, homeEmail, supervisorEId, location, workPhone, mobilePhone, hireDate, dateOfBirth, status. No compensation, no time-off balances, no performance data, no documents, no custom tables. Nothing is ever written back to BambooHR: every call is a GET.

Terminations. BambooHR soft-terminates by setting status to Inactive. Those people stop appearing in the directory and the chart on the next sync. A hard delete arrives in the changed feed as Deleted and removes the record outright.

Credential handling. The API key is encrypted app-side with AES-256-GCM before it's stored. The encryption key lives outside the database, so the database alone can't yield your credential. The key is write-only: the browser never receives it back, and an untouched key field on later edits means "keep what's stored." Rotation is paste-over-and-save.

Throttling. Retry-After headers are honored; the sync backs off rather than hammering the API. Employee records are fetched a few at a time, not all at once.

Troubleshooting

  • Test says the directory rejected the credentials: the key was pasted with whitespace, or it was revoked in BambooHR. Generate a fresh one.
  • Test can't reach the directory: the subdomain is wrong. It's the name in your own BambooHR address, not your email domain.
  • Fewer people than you expect: the account that generated the key can't see everyone. Regenerate from an account with full directory visibility.
  • People are missing managers: supervisorEId is empty for them in BambooHR itself. Sprooster shows what the API returns; anyone without a supervisor renders as a root of the chart.
  • Birthdays are missing: dateOfBirth is often left blank in BambooHR, and a blank one is treated as "not shared" rather than guessed.

On this page