/v1/salary, appended to the gateway base URLs listed in the API reference introduction.
The salary history itself lives on the employee, not here. Read it with
GET /employees/{employeeId}/salary and change it with PUT /employees/{employeeId}/salary in the Pay API. This service only decides what that history is worth in a given pay period.Key concepts
- Salary history — the employee’s annual salary as a list of effective-dated versions, owned by the Pay API. The version in force on a day is the one with the latest
effectiveFromon or before it. - Enablement — the organisation configuration’s
enabledflag. While it isfalse, or while no organisation configuration exists, the service is dormant: it posts nothing to a pay run and changes nothing already there. Turning it on is what starts salary lines appearing. - Calculation method —
CalendarDaysorWorkingDays, deciding which days of a part period count. Calendar days count every day; working days count only the days the employee works, taken from their working pattern. - Method resolution — the method for an employee is the first of: their own employee configuration, then the organisation configuration, then a built-in default of
CalendarDays. Every response that resolves a method says which level answered, insource:Employee,OrganisationorDefault. - Segment — a stretch of the pay period over which one salary version and one employment status apply. A period with no change has one segment; a salary change part-way through splits the period into two, and each becomes its own line.
- Divisor — for a part period, the number of qualifying days of the same kind in the UK tax year (6 April to 5 April) containing the segment’s first day. That is 365 or 366 for calendar days, and the employee’s own working days counted through the tax year for working days — so a three-day week divides by roughly 156, not 260.
- Salary line — a line item posted onto the pay run, with an id starting
internal_salary_. It is posted withcanEdit: false, so it is recalculated rather than edited; adjustments are separate manual lines.
API structure
Organisation configuration
Set how the organisation pro-rates salary, and whether the service runs at all, at/organisation. GET returns the configuration and 404s when there is none, PUT creates or replaces it with calculationMethod and enabled, and DELETE removes it and returns the configuration it removed (404 when there was none) — after which the service goes dormant for the organisation.
Employee configuration
Override the method for one employee at/employees/{employeeId}. GET always returns 200: an employee with no override has calculationMethod: null alongside the effectiveCalculationMethod and the source it came from. PUT sets the override, and DELETE removes it so the employee falls back to the organisation, returning the configuration now in force as the GET would.
Statutory leave
Decide what happens to the salary on the days an employee is paid statutory pay, at/organisation/leave and /employees/{employeeId}/leave. The document has sickness (Statutory Sick Pay), familyLeave (the six family-leave payments) and byType (an exception for one family-leave payment, keyed by its name, such as StatutoryPaternityPay). Each policy is DeductAbsentDays (the default), with optional top-up tiers, each a duration ({ "value": 6, "unit": "Week" }) and a percentage of salary, or FullPay, which tops every day of the leave up to 100% and takes no tiers. Both take the days on leave back. GET always returns 200 with the stored document and effective, the setting each of the seven payments resolves to and its source. PUT replaces the whole document, and DELETE removes it and returns what now applies. See Salary during statutory leave for how the adjustment and top-up are worked out.
Dry run
POST /employees/{employeeId}/calculate works out what a period would produce and returns it without posting anything. Send payrollConfigId, periodStartDate, periodEndDate and frequency; get back the resolved calculationMethod, its source, and a line per segment carrying units, payRate, amount, the divisor used, and the segmentStart and segmentEnd it covers. Use it to check a joiner, a leaver or a mid-period rise before the pay run is opened.
To preview statutory leave, add statutoryLines: each one’s statutoryType, units, unitName and amount, and the from and to dates it pays for. Add leaveStart when a top-up has tiers, since they count from it. The response then also carries the adjustment and any top-up, told apart by kind (Salary, StatutoryAbsenceAdjustment or StatutoryTopUp) and naming the statutoryType they respond to.
Typical workflow
- Turn the service on for the organisation with
PUT /organisation, choosingcalculationMethodandenabled: true. - Override the method for any employee whose pro-rata differs, with
PUT /employees/{employeeId}. - Record salary against the employee in the Pay API with
PUT /employees/{employeeId}/salary, dated from when it applies. - Check an awkward period with
POST /employees/{employeeId}/calculatebefore the pay run opens — a mid-month joiner, or a rise part-way through. - Set what happens during statutory leave with
PUT /organisation/leave, if the default (deduct the days on leave, no top-up) is not your policy. - Open the pay run. Salary lines are posted as each payslip is previewed, and re-checked on every later preview.
How a period is worked out
1
The employment window
The period is narrowed to the days the employee was actually employed: from the later of the period start and their start date, to the earlier of the period end and their leaving date. An employee employed for none of the period gets no lines.
2
Segments
The window is split at each salary
effectiveFrom that falls inside it. Days before the employee’s first salary version are unpaid, and a segment whose salary is 0 produces no line.3
A full period
One segment covering the whole period is paid as a fraction of the year: the annual salary divided by the number of periods in the year — 52 weekly, 26 fortnightly, 13 four-weekly, 12 monthly, 4 quarterly, 2 twice-yearly, 1 annually. The line is a month, a week or a year of salary, not a count of days.
4
A part period
Anything else is paid per day: the qualifying days in the segment, at a rate of the annual salary divided by the qualifying days in the tax year. Which days qualify is the calculation method’s answer, and both sides of the division use the same kind of day, so the two always agree.
Notes and best practices
Changes reach open pay runs asynchronously
A configuration or salary write returns as soon as it is saved. Updating the salary lines on open pay runs happens afterwards, in the background, so don’t read the payslip straight after a write and expect the new lines to be there.
- Every write triggers its own recalculation. You don’t need to call anything to refresh a pay run after changing a configuration or a salary. An employee-level change recalculates that employee’s preview payslips on every open pay run they’re on. An organisation-level change recalculates every employee, so on a large organisation expect the updates to finish over a short period rather than all at once.
- Poll the payslip, not the configuration. To confirm a change has landed, read the employee’s payslip lines on the pay run and look for the updated salary line. The configuration endpoints only tell you what is saved.
- Check lines before you lock. Locking waits for recalculations already in progress, but nothing is written to a period once it is locked. After a batch of changes, confirm the payslip lines are what you expect before you lock the pay run.
- Use the dry run to check figures.
POST /employees/{employeeId}/calculateuses the same configuration and employee record as the pay run, so its figures match what will be posted, and it returns them straight away without writing anything.
Working with salary lines
- Identify lines by their id prefix. Salary lines start
internal_salary_, statutory leave adjustmentsinternal_salary_adjustment_, and top-upsinternal_salary_topup_. Filter on these prefixes to tell salary lines apart from the rest of a payslip. Treat the part after the prefix as opaque. - Ids are stable across recalculations. The same employee, period and segment always produce the same id, so recalculating replaces a line rather than duplicating it, and you can match lines between reads by id.
- Don’t edit salary lines — add to them. They’re posted with
canEdit: false, like statutory payments, so they can always be recalculated from the salary history. To pay more or less than the calculated salary, add a separate manual line. - No salary lines isn’t an error. An employee with no salary version, or a period that starts after their leaving date, gets no salary lines. Pay a late bonus or final holiday pay as its own line.
- Working days follow the working pattern. The
WorkingDaysmethod uses the employee’s own pattern, then the pay run’s default, then the organisation’s, then Monday to Friday. See Working patterns.
Requests and responses
GETon employee and leave configuration always returns200. ReadeffectiveCalculationMethodoreffective, and itssource, to find out what applies — you don’t need to handle a404or walk the fallback yourself. OnlyGET /organisationreturns404when nothing is configured.DELETEreturns what now applies. A delete answers200with the response envelope rather than204, so you don’t need a second request to see the fallback now in force.PUT /…/leavereplaces the whole document. Send every policy you want to keep, not just the one you’re changing — anything you leave out falls back to the next level, aseffectiveshows.
Authentication
Every request needs anX-Auth-Token header, and credentials with access to more than one organisation also send X-Org-Id — see Authentication. The organisation is always taken from those credentials, never from a request body. Responses use the shared envelope described in Response format.