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
- Create a dedicated BambooHR API user or OAuth application with only the permissions required by the intended workflows.
- Store the API key or current OAuth access token only in the encrypted
apiKeyfield of a ValkyrAIIntegrationAccountand set the account toREADY. - Bind that account through
ExecModuleConfig.authConfig.integrationAccount. - Set
companySubdomainto the company label from<companySubdomain>.bamboohr.com, without a scheme, path, port, query, or credentials. - Set
authModetoapi_keyfor preemptive Basic authentication oroauth_bearerfor a current OAuth token. - Choose one operation and provide its conditional fields.
- For employee lists, pass
nextCursorinto a later execution ascursorwhenhasMoreistrue.
Inputs
| Name | Type | Required | Description | Constraints |
|---|---|---|---|---|
operation | string | Yes | One of the nine operations below. | Exact allowlist. |
companySubdomain | string | Yes | Company label in the BambooHR tenant hostname. | Lowercase letters, digits, and internal hyphens; 1–63 characters; no arbitrary URL. |
authMode | string | No | Credential protocol. | api_key by default, or oauth_bearer. |
employeeId | string | Conditional | Internal BambooHR employee ID. | Positive integer. 0 is accepted only by get_employee for the authenticated user's own employee record. |
record | object | Conditional | Employee fields for create/update. | Non-empty; at most 20 allowlisted scalar/null fields and 128 KiB serialized. |
fields | array or string | No | Additional employee field aliases. | 1–50 safe aliases; JSON array or comma-separated string. |
filters | object | No | Filters for list_employees. | At most 10 allowlisted scalar values, each at most 2,048 characters. |
sort | string | No | Employee sort order. | At most five comma-separated supported fields, optionally prefixed with -. |
cursor | string | No | Opaque BambooHR page[after] value. | 1–2,048 safe cursor characters; never construct or modify it. |
limit | integer | No | Maximum normalized items. | 1–250; default 100. |
onlyCurrent | boolean | No | Restrict effective-dated employee fields to current values. | Default true. |
showInactive | boolean | No | Include future/inactive directory entries when BambooHR allows it. | Default false; used only by get_directory. |
startDate | date | Conditional | Inclusive time-off window start. | YYYY-MM-DD. |
endDate | date | Conditional | Inclusive time-off window end. | YYYY-MM-DD, on or after startDate, within five years. |
action | string | No | Time-off list audience. | view, approve, or myRequests; default view. |
timeOffStatus | string | No | Time-off status filter or new-request status. | Lists accept known request states. Creates accept requested, approved, denied, or declined. |
timeOffTypeId | string | Conditional | Time-off type for a new request. | Positive integer ID. |
amount | number | No | Requested hours/days. | Greater than 0 and at most 10,000. |
note | string | No | One bounded note for a new time-off request. | At most 2,000 safe characters. |
excludeNote | boolean | No | Omit notes from time-off list results. | Default true to minimize sensitive output. |
timeoutMs | integer | No | Provider call timeout. | 100–300,000; default 30,000. |
confirmWrite | boolean | Conditional | Explicit mutation confirmation. | Must be true for all three write operations. |
Employee record writes allow only:
firstName,lastName,preferredName,workEmailjobTitle,department,division,locationhireDate,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
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation. |
resource | string | After operation validation | company, employees, directory, fields, or time-off. |
data | object | Single-resource/action success | Bounded provider object or normalized action evidence. |
items | array | Collection success | Up to limit provider records. |
count | integer | Collection success | Items returned in this execution. |
total | integer | Employee-list success when provided | BambooHR's permission-aware total for the filtered query. |
hasMore | boolean | Collection success | Whether list_employees returned another opaque cursor. Other list operations return false. |
nextCursor | string | Another employee page exists | Opaque cursor to pass unchanged to the next execution. |
httpStatus | integer | Provider success | Provider HTTP status. |
attempts | integer | Provider call or error | Total HTTP attempts consumed. |
requestId | string | Provider supplied one | Bounded provider request correlation ID. |
error | object | Failure | Redacted 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
| Setting | Requirement |
|---|---|
| Provider | BambooHR API v1 |
| Status | Exactly READY |
accountName | Human-readable tenant and API-user identity |
apiKey | Encrypted BambooHR API key or current OAuth access token, 16–4,096 non-whitespace characters |
| Relationship | ExecModuleConfig.authConfig.integrationAccount |
| Tenant binding | Separate validated companySubdomain parameter |
Authentication modes:
api_key: sendsAuthorization: Basic base64(apiKey:x)on the first request. The API key inherits the associated BambooHR user's permissions.oauth_bearer: sendsAuthorization: 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
| Operation | Method and path | Required input | Behavior |
|---|---|---|---|
get_company_info | GET /api/v1/company_information | — | Returns basic company profile information. |
list_employees | GET /api/v1/employees | — | Returns one cursor page with bounded fields, filters, and sort. |
get_employee | GET /api/v1/employees/{id} | employeeId | Requests explicit fields and verifies the returned internal ID. |
create_employee | POST /api/v1/employees | record, confirmWrite=true | Creates one employee once; requires first and last name. |
update_employee | POST /api/v1/employees/{id} | employeeId, record, confirmWrite=true | Updates allowlisted fields once. |
get_directory | GET /api/v1/employees/directory | — | Reads the configured public-within-company directory subset. |
list_fields | GET /api/v1/meta/fields | — | Discovers standard and custom employee field aliases. |
list_time_off_requests | GET /api/v1/time_off/requests | startDate, endDate | Reads a bounded date window with permission-aware filters. |
create_time_off_request | PUT /api/v1/employees/{id}/time_off/request | employee/date/type fields, confirmWrite=true | Creates 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
| Failure | Cause | Retry guidance |
|---|---|---|
VALIDATION_ERROR | Missing 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_REQUIRED | No bound secure account. | Bind the intended BambooHR account. |
INTEGRATION_ACCOUNT_NOT_READY | Bound account is not READY. | Repair or reauthorize the account first. |
BAMBOOHR_HTTP_400/409 | Invalid field, cursor, date, duplicate value, or incompatible HR value. | Use list_fields and BambooHR configuration to correct the input. |
BAMBOOHR_HTTP_401/403 | Invalid credential, missing OAuth scope, or insufficient employee/field permission. | Rotate/re-authorize the credential or reduce the requested operation and fields. |
BAMBOOHR_HTTP_404 | Employee/resource missing, unsupported format, or empty/disabled directory behavior. | Verify the internal ID and tenant feature configuration. |
BAMBOOHR_HTTP_429 | Provider rate limit. | Safe reads retry up to three attempts and honor bounded numeric Retry-After; writes do not retry. |
BAMBOOHR_HTTP_5xx / NETWORK_ERROR | Provider or transport failure. | Reads retry up to three attempts. Reconcile every employee/time-off write before manual retry. |
RESPONSE_TOO_LARGE | Provider 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_employeessendspage[limit]and optionalpage[after]. It returns at most 250 records and never silently follows another page. Other collection endpoints are normalized tolimitafter 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-Afteris 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.