Skip to main content

BambooHR ExecModule

Overview

BambooHrModule connects ValkyrAI workflows to the tenant-scoped BambooHR API v1 for employee records, the published company directory, employee-field discovery, basic company information, and time-off requests. It exposes nine bounded operations through the native map I/O ExecModule ABI and keeps the API key or OAuth access token in a READY IntegrationAccount.

The module accepts a company subdomain rather than an arbitrary URL, sends credentials preemptively, limits employee fields and filters, returns one cursor page per execution, caps request JSON at 128 KiB and provider responses at 5 MiB, retries transient failures only for safe GET operations, and requires confirmWrite=true for every HR mutation. HR results are classified restricted; credentials and record values are never included in progress logs.

The official n8n BambooHR node informed employee CRUD, report, and document use cases. ValkyrAI uses BambooHR's newer direct tenant URL and cursor-paginated employee endpoint, adds company and time-off operations, excludes binary document handling, and adds a generated IntegrationAccount boundary, trusted tenant routing, explicit write confirmation, response identity checks, bounded retries, normalized outputs, and credential redaction.

Usage

  1. Create a dedicated BambooHR API user or OAuth application with only the permissions required by the intended workflows.
  2. Store the API key or current OAuth access token only in the encrypted apiKey field of a ValkyrAI IntegrationAccount and set the account to READY.
  3. Bind that account through ExecModuleConfig.authConfig.integrationAccount.
  4. Set companySubdomain to the company label from <companySubdomain>.bamboohr.com, without a scheme, path, port, query, or credentials.
  5. Set authMode to api_key for preemptive Basic authentication or oauth_bearer for a current OAuth token.
  6. Choose one operation and provide its conditional fields.
  7. For employee lists, pass nextCursor into a later execution as cursor when hasMore is true.

Inputs

NameTypeRequiredDescriptionConstraints
operationstringYesOne of the nine operations below.Exact allowlist.
companySubdomainstringYesCompany label in the BambooHR tenant hostname.Lowercase letters, digits, and internal hyphens; 1–63 characters; no arbitrary URL.
authModestringNoCredential protocol.api_key by default, or oauth_bearer.
employeeIdstringConditionalInternal BambooHR employee ID.Positive integer. 0 is accepted only by get_employee for the authenticated user's own employee record.
recordobjectConditionalEmployee fields for create/update.Non-empty; at most 20 allowlisted scalar/null fields and 128 KiB serialized.
fieldsarray or stringNoAdditional employee field aliases.1–50 safe aliases; JSON array or comma-separated string.
filtersobjectNoFilters for list_employees.At most 10 allowlisted scalar values, each at most 2,048 characters.
sortstringNoEmployee sort order.At most five comma-separated supported fields, optionally prefixed with -.
cursorstringNoOpaque BambooHR page[after] value.1–2,048 safe cursor characters; never construct or modify it.
limitintegerNoMaximum normalized items.1–250; default 100.
onlyCurrentbooleanNoRestrict effective-dated employee fields to current values.Default true.
showInactivebooleanNoInclude future/inactive directory entries when BambooHR allows it.Default false; used only by get_directory.
startDatedateConditionalInclusive time-off window start.YYYY-MM-DD.
endDatedateConditionalInclusive time-off window end.YYYY-MM-DD, on or after startDate, within five years.
actionstringNoTime-off list audience.view, approve, or myRequests; default view.
timeOffStatusstringNoTime-off status filter or new-request status.Lists accept known request states. Creates accept requested, approved, denied, or declined.
timeOffTypeIdstringConditionalTime-off type for a new request.Positive integer ID.
amountnumberNoRequested hours/days.Greater than 0 and at most 10,000.
notestringNoOne bounded note for a new time-off request.At most 2,000 safe characters.
excludeNotebooleanNoOmit notes from time-off list results.Default true to minimize sensitive output.
timeoutMsintegerNoProvider call timeout.100–300,000; default 30,000.
confirmWritebooleanConditionalExplicit mutation confirmation.Must be true for all three write operations.

Employee record writes allow only:

  • firstName, lastName, preferredName, workEmail
  • jobTitle, department, division, location
  • hireDate, employmentHistoryStatus, employeeNumber, supervisor

create_employee requires non-empty firstName and lastName. Sensitive fields such as SSNs, tax identifiers, credentials, passwords, and tokens are intentionally not writable. BambooHR custom fields, payroll fields, photos, dependents, compensation, and benefits are deferred.

Employee list filters allow firstName, lastName, status, jobTitleName, departmentName, locationName, and ids. Sort fields allow employeeId, firstName, lastName, preferredName, jobTitleName, and status.

Outputs

NameTypeWhen presentDescription
statusstringAlwayssuccess or error.
operationstringAlwaysNormalized operation.
resourcestringAfter operation validationcompany, employees, directory, fields, or time-off.
dataobjectSingle-resource/action successBounded provider object or normalized action evidence.
itemsarrayCollection successUp to limit provider records.
countintegerCollection successItems returned in this execution.
totalintegerEmployee-list success when providedBambooHR's permission-aware total for the filtered query.
hasMorebooleanCollection successWhether list_employees returned another opaque cursor. Other list operations return false.
nextCursorstringAnother employee page existsOpaque cursor to pass unchanged to the next execution.
httpStatusintegerProvider successProvider HTTP status.
attemptsintegerProvider call or errorTotal HTTP attempts consumed.
requestIdstringProvider supplied oneBounded provider request correlation ID.
errorobjectFailureRedacted code, message, and retryable fields.

Employee, directory, company, field, and time-off payloads are classified restricted. BambooHR can silently suppress field values based on the API user's permissions. A null, absent, or filtered field is not proof that the underlying employee or value does not exist.

IntegrationAccount Requirements

SettingRequirement
ProviderBambooHR API v1
StatusExactly READY
accountNameHuman-readable tenant and API-user identity
apiKeyEncrypted BambooHR API key or current OAuth access token, 16–4,096 non-whitespace characters
RelationshipExecModuleConfig.authConfig.integrationAccount
Tenant bindingSeparate validated companySubdomain parameter

Authentication modes:

  • api_key: sends Authorization: Basic base64(apiKey:x) on the first request. The API key inherits the associated BambooHR user's permissions.
  • oauth_bearer: sends Authorization: Bearer <token>. This version expects a current access token; OAuth authorization and refresh lifecycle remain outside the module.

Minimum OAuth scopes depend on enabled operations. Typical read flows need company:info, employee, employee:name, employee:job, employee_directory, field, and time_off. Employee mutations need the corresponding employee.write and field-family write scopes. Creating time off requires time_off.write. Use the narrowest applicable set and a dedicated integration identity.

Configuration

Illustrative persisted configuration:

{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:bamboohr-people-ops"
},
"payloadConfig": {
"parameters": "{\"companySubdomain\":\"valkyr-labs\",\"authMode\":\"api_key\",\"operation\":\"list_employees\",\"limit\":100}"
}
}

The relationship is symbolic. The generated IntegrationAccount owns the encrypted credential. Never put an API key, OAuth token, employee secret, or signed photo URL into module parameters, filters, workflow state, examples, or logs.

Operations

OperationMethod and pathRequired inputBehavior
get_company_infoGET /api/v1/company_informationReturns basic company profile information.
list_employeesGET /api/v1/employeesReturns one cursor page with bounded fields, filters, and sort.
get_employeeGET /api/v1/employees/{id}employeeIdRequests explicit fields and verifies the returned internal ID.
create_employeePOST /api/v1/employeesrecord, confirmWrite=trueCreates one employee once; requires first and last name.
update_employeePOST /api/v1/employees/{id}employeeId, record, confirmWrite=trueUpdates allowlisted fields once.
get_directoryGET /api/v1/employees/directoryReads the configured public-within-company directory subset.
list_fieldsGET /api/v1/meta/fieldsDiscovers standard and custom employee field aliases.
list_time_off_requestsGET /api/v1/time_off/requestsstartDate, endDateReads a bounded date window with permission-aware filters.
create_time_off_requestPUT /api/v1/employees/{id}/time_off/requestemployee/date/type fields, confirmWrite=trueCreates one request once.

Employee deletion is permanently destructive and not exposed. Time-off superseding through previousRequest is also excluded because it can delete approvals, workflow state, and notifications. Binary employee files, photos, company files, saved reports, datasets, benefits, payroll, goals, webhooks, and time tracking are deferred.

Errors and Failure Modes

FailureCauseRetry guidance
VALIDATION_ERRORMissing or malformed tenant, ID, field, filter, sort, cursor, date, payload, confirmation, success shape, or identity.Correct the request. Validation failures send no provider call.
INTEGRATION_ACCOUNT_REQUIREDNo bound secure account.Bind the intended BambooHR account.
INTEGRATION_ACCOUNT_NOT_READYBound account is not READY.Repair or reauthorize the account first.
BAMBOOHR_HTTP_400/409Invalid field, cursor, date, duplicate value, or incompatible HR value.Use list_fields and BambooHR configuration to correct the input.
BAMBOOHR_HTTP_401/403Invalid credential, missing OAuth scope, or insufficient employee/field permission.Rotate/re-authorize the credential or reduce the requested operation and fields.
BAMBOOHR_HTTP_404Employee/resource missing, unsupported format, or empty/disabled directory behavior.Verify the internal ID and tenant feature configuration.
BAMBOOHR_HTTP_429Provider rate limit.Safe reads retry up to three attempts and honor bounded numeric Retry-After; writes do not retry.
BAMBOOHR_HTTP_5xx / NETWORK_ERRORProvider or transport failure.Reads retry up to three attempts. Reconcile every employee/time-off write before manual retry.
RESPONSE_TOO_LARGEProvider response exceeded 5 MiB.Narrow the fields, filters, date window, or page size.

Provider error text is bounded to 500 characters and passes through exact credential replacement plus the shared sensitive-data policy. A network failure after a mutation may be ambiguous, so the connector never automatically replays writes.

Example

List active employees with two additional fields:

{
"companySubdomain": "valkyr-labs",
"operation": "list_employees",
"fields": ["workEmail", "department"],
"filters": {
"status": "active"
},
"sort": "lastName,firstName",
"limit": 100
}

Expected normalized result:

{
"status": "success",
"operation": "list_employees",
"resource": "employees",
"items": [
{
"employeeId": "42",
"firstName": "Ada",
"lastName": "Lovelace",
"workEmail": "ada@example.com",
"department": "Engineering"
}
],
"count": 1,
"total": 1,
"hasMore": false,
"httpStatus": 200,
"attempts": 1
}

If hasMore is true, call the module again with the returned nextCursor as cursor. Treat the cursor as opaque.

Notes

  • Pagination: list_employees sends page[limit] and optional page[after]. It returns at most 250 records and never silently follows another page. Other collection endpoints are normalized to limit after one provider response.
  • Permission-aware results: BambooHR may return null restricted fields, omit unauthorized fields, or filter rows when the caller cannot read a filter/sort field. Do not calculate authoritative headcount, department totals, turnover, or other HR aggregates from a partial permission set.
  • Directory behavior: the directory is controlled by each company's sharing configuration and may return 403 or 404 instead of an empty list. It is not a complete employee roster.
  • Rate limits: HTTP 429, 408, and selected 5xx responses are transient only for safe reads. Numeric Retry-After is honored within a five-second local cap.
  • API limits: field lists are capped at 50, filters at 10, cursor length at 2,048, request JSON at 128 KiB, and response JSON at 5 MiB. BambooHR may enforce stricter tenant- or scope-specific limits.
  • Idempotency: reads are retry-safe. Employee creates, employee updates, and time-off requests are single-attempt and require explicit confirmation. Reconcile BambooHR state after any ambiguous timeout.
  • Destructive behavior: employee deletion and time-off superseding are intentionally absent. Updates can still affect payroll, onboarding, approvals, permissions, notifications, and downstream HR integrations.
  • Privacy: use a least-privilege account and request only needed fields. Never use the connector to infer missing sensitive values or to bypass BambooHR's employee-level and field-level permissions.
  • External verification: deterministic tests cover validation, tenant routing, Basic/OAuth headers, employee cursor pagination, field/filter encoding, employee CRUD bodies, time-off dates and bodies, read retry, write non-retry, redaction, response identities, and annotation discovery. Live provider execution is deferred until separately authorized BambooHR credentials are supplied.

See the official BambooHR API documentation, List Employees reference, Time Off reference, authentication history and OAuth guidance, and n8n BambooHR node for upstream behavior.