@markwharton/eh-payroll
v3.4.0
Published
Employment Hero Payroll API client
Downloads
2,560
Readme
@markwharton/eh-payroll
Employment Hero Payroll API client for employee data, roster shifts, and time & attendance.
Install
npm install @markwharton/eh-payrollQuick Start
import { PayrollClient } from '@markwharton/eh-payroll';
const client = new PayrollClient({
apiKey: 'xxx',
businessId: 123,
region: 'au', // 'au' | 'nz' | 'uk' | 'sg' | 'my' (default: 'au')
});
// Validate credentials
const validation = await client.validateApiKey();
if (!validation.ok) throw new Error(validation.error);
// Get all employees
const empResult = await client.getEmployees();
if (empResult.ok) console.log(empResult.data); // PayrollAuEmployee[]
// Get employees filtered by location
const filteredResult = await client.getEmployees({ locationId: 5 });
if (filteredResult.ok) console.log(filteredResult.data); // PayrollAuEmployee[]
// Get roster shifts for a date range (auto-paginates)
const rosterResult = await client.getRosterShifts('2026-02-03', '2026-02-09');
if (rosterResult.ok) console.log(rosterResult.data); // PayrollRosterShift[]
// Get roster shifts with filters
const pubResult = await client.getRosterShifts('2026-02-03', '2026-02-09', {
employeeId: 42,
shiftStatus: 'Published',
selectAllRoles: true
});
if (pubResult.ok) console.log(pubResult.data); // PayrollRosterShift[]
// Get leave requests for a date range (all filters server-side)
const leaveResult = await client.getLeaveRequests({
fromDate: '2026-01-01',
toDate: '2026-03-31',
status: 'Approved',
});
if (leaveResult.ok) console.log(leaveResult.data); // PayrollLeaveRequest[]
// Get business locations
const locResult = await client.getLocations();
if (locResult.ok) console.log(locResult.data); // PayrollLocation[]
// List kiosks
const kioskResult = await client.getKiosks();
if (kioskResult.ok) console.log(kioskResult.data); // PayrollKiosk[]
// Get kiosk staff (time and attendance)
const staffResult = await client.getKioskStaff(kioskId);
if (staffResult.ok) console.log(staffResult.data); // PayrollKioskEmployee[]
// Get employee groups
const groupResult = await client.getEmployeeGroups();
if (groupResult.ok) console.log(groupResult.data); // PayrollEmployeeGroup[]
// Get standard hours for an employee (includes FTE value)
const hoursResult = await client.getStandardHours(employeeId);
if (hoursResult.ok) console.log(hoursResult.data); // PayrollStandardHours
// Get leave balances for an employee
const balanceResult = await client.getEmployeeLeaveBalances(employeeId);
if (balanceResult.ok) console.log(balanceResult.data); // PayrollLeaveBalance[]
Result Pattern
All methods return Result<T> — see api-core Result Pattern. Always check ok before accessing data.
API Reference
| Method | Parameters | Returns |
|--------|-----------|---------|
| validateApiKey() | — | Result<void> |
| getEmployees(options?) | PayrollEmployeeOptions? | Result<PayrollAuEmployee[]> |
| getEmployee(employeeId) | number | Result<PayrollAuEmployee> |
| getEmployeeImage(employeeId) | number | Result<PayrollEmployeeImage> |
| getStandardHours(employeeId) | number | Result<PayrollStandardHours> |
| getEmployeeLeaveBalances(employeeId, options?) | number, PayrollLeaveBalanceOptions? | Result<PayrollLeaveBalance[]> |
| getUnavailabilities(options?) | PayrollUnavailabilityOptions? | Result<PayrollUnavailability[]> |
| getLocations() | — | Result<PayrollLocation[]> |
| getEmployeeGroups() | — | Result<PayrollEmployeeGroup[]> |
| getPublicHolidays(options?) | PayrollPublicHolidayOptions? | Result<PayrollPublicHoliday[]> |
| getWorkTypes() | — | Result<PayrollWorkType[]> |
| getLeaveCategories() | — | Result<PayrollLeaveCategory[]> |
| getRosterShifts(from, to, options?) | string, string, PayrollRosterShiftOptions? | Result<PayrollRosterShift[]> |
| getKiosks() | — | Result<PayrollKiosk[]> |
| getKioskStaff(kioskId, options?) | number, PayrollKioskStaffOptions? | Result<PayrollKioskEmployee[]> |
| getLeaveRequests(options?) | PayrollLeaveRequestOptions? | Result<PayrollLeaveRequest[]> |
| getEmployeeLeaveRequests(employeeId, options?) | number, PayrollEmployeeLeaveRequestOptions? | Result<PayrollLeaveRequest[]> |
| clearCache() | — | void |
| invalidateEmployeeCache() | — | void |
| invalidateLeaveRequestCache() | — | void |
| invalidateRosterShiftCache() | — | void |
| invalidateLocationCache() | — | void |
| invalidateKioskCache() | — | void |
getLeaveRequests()
Returns leave requests with all filters applied server-side. The business-level endpoint returns a flat array — no pagination.
Leave requests are time-series data — unlike employees and locations which remain relatively stable, leave requests grow continuously. Always use fromDate/toDate filters to bound the result set.
PayrollLeaveRequestOptions:
| Option | Type | Description |
|--------|------|-------------|
| fromDate | string | Start date filter (YYYY-MM-DD) |
| toDate | string | End date filter (YYYY-MM-DD) |
| status | PayrollLeaveRequestStatus | 'Approved', 'Pending', 'Rejected', or 'Cancelled' |
| employeeId | number | Filter by employee ID |
| leaveCategoryId | number | Filter by leave category ID |
| locationId | number | Filter by location ID |
| groupBy | 'Employee' \| 'LeaveType' | Group results by employee or leave type |
| restrictOverlappingLeave | boolean | Restrict results to leave overlapping the date range |
getEmployeeLeaveRequests()
Returns leave requests for a specific employee using the per-employee endpoint with OData pagination.
PayrollEmployeeLeaveRequestOptions:
| Option | Type | Description |
|--------|------|-------------|
| status | PayrollLeaveRequestStatus | 'Approved', 'Pending', 'Rejected', or 'Cancelled' |
| fromDate | string | Start date filter (ISO 8601) |
| toDate | string | End date filter (ISO 8601) |
getEmployeeLeaveBalances()
Returns the accrued leave balance per category for an employee. The balance is the "live" value calculated from pay runs — updated after each pay run is processed. Supports an optional asAtDate parameter for point-in-time balance queries.
PayrollLeaveBalanceOptions:
| Option | Type | Description |
|--------|------|-------------|
| asAtDate | string | Point-in-time date for balance query (ISO 8601 date-time) |
getUnavailabilities()
Returns unavailability records for the business. The endpoint returns a flat array (not paginated).
Unavailabilities are time-series data — always use fromDate/toDate filters to bound the result set.
PayrollUnavailabilityOptions:
| Option | Type | Description |
|--------|------|-------------|
| fromDate | string | Start date filter (YYYY-MM-DD) |
| toDate | string | End date filter (YYYY-MM-DD) |
| employeeId | number | Filter by employee ID |
| defaultLocationId | number | Filter by default location ID |
getPublicHolidays()
Returns public holidays for the business.
PayrollPublicHolidayOptions:
| Option | Type | Description |
|--------|------|-------------|
| year | number | Filter by year |
getWorkTypes()
Returns all work types for the business. Automatically paginates through all results.
getLeaveCategories()
Returns all leave categories for the business. Automatically paginates through all results.
getRosterShifts()
Automatically paginates through all results (100 per page). Returns the complete set of roster shifts for the requested date range. All filter options are applied server-side, reducing the number of results and pages fetched.
Query Parameter Casing
The KeyPay Swagger spec uses inconsistent query parameter casing across endpoints. Roster shifts and leave requests use PascalCase (EmployeeId, ShiftStatus, FromDate), while other endpoints use camelCase (selectedColumns, filter.locationId). The library accepts camelCase options throughout and converts to PascalCase internally where needed.
Configuration
const client = new PayrollClient({
apiKey: 'your-api-key', // Required: used as Basic Auth username
businessId: 12345, // Required: business ID
region: 'au', // Optional: au, nz, uk, sg, my (default: au)
baseUrl: '...', // Optional: override base URL (takes priority over region)
rateLimitPerSecond: 5, // Optional: rate limit (default 5, set 0 to disable)
onRequest: ({ method, url, description }) => { ... }, // Optional: debug callback
cache: {}, // Optional: enable TTL caching (defaults below)
cacheInstance: cache, // Optional: custom cache backend (e.g., LayeredCache)
retry: { // Optional: retry on 429/503
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 10000,
},
});PayrollConfig extends ClientConfig from api-core, which provides the baseUrl, onRequest, retry, and cacheInstance fields. apiKey, businessId, region, cache, and rateLimitPerSecond are payroll-specific. Pass cacheInstance to use a custom cache backend (e.g., LayeredCache with persistent stores); otherwise cache: {} creates an in-memory TTLCache. Set persistRestricted: false to keep restricted-tier data in memory only.
Cache TTLs
| Cache Key | Default TTL | |-----------|-------------| | Employees | 5 min | | Locations | 5 min | | Employee groups | 5 min | | Standard hours | 5 min | | Roster shifts | 2 min | | Leave requests | 2 min | | Kiosks | 5 min | | Kiosk staff | 1 min |
Failed API results (ok: false) are never cached — transient errors won't persist for the full TTL. See the root README Cache System section for the full cache architecture (layered stores, restricted data handling, request coalescing).
Rate Limiting
The client enforces a sliding-window rate limit of 5 requests per second (the API limit). All outbound requests pass through the rate limiter automatically. Set rateLimitPerSecond: 0 in config to disable (useful for tests). The RateLimiter class is provided by api-core and operates per-instance (see Known Limitations in the root README).
Retry
Automatically retries on HTTP 429 (Too Many Requests) and 503 (Service Unavailable) with exponential backoff. Respects the Retry-After header when present.
Authentication
Uses HTTP Basic Authentication with the API key as the username and an empty password, per the KeyPay API reference.
License
MIT
