Skip to main content
The Salary API turns the annual salary you record against an employee into the lines that appear on their pay run. It reads the effective-dated salary history held on the employee in the Pay API, works out what is owed for the days they were employed in the pay period, and posts the result onto the open pay run. You configure how the pro-rata is worked out and what happens to the salary during statutory leave, and call a dry run to see what a period would produce before it is produced. For the product view, see Salary. All endpoint paths on this page are relative to the service base path /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 effectiveFrom on or before it.
  • Enablement — the organisation configuration’s enabled flag. While it is false, 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 — CalendarDays or WorkingDays, 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, in source: Employee, Organisation or Default.
  • 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 with canEdit: 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

  1. Turn the service on for the organisation with PUT /organisation, choosing calculationMethod and enabled: true.
  2. Override the method for any employee whose pro-rata differs, with PUT /employees/{employeeId}.
  3. Record salary against the employee in the Pay API with PUT /employees/{employeeId}/salary, dated from when it applies.
  4. Check an awkward period with POST /employees/{employeeId}/calculate before the pay run opens — a mid-month joiner, or a rise part-way through.
  5. 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.
  6. 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.
A salary change part-way through a period therefore produces two lines — the old salary to the day before the change, the new one from it — and each says so in its description.

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}/calculate uses 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 adjustments internal_salary_adjustment_, and top-ups internal_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 WorkingDays method 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

  • GET on employee and leave configuration always returns 200. Read effectiveCalculationMethod or effective, and its source, to find out what applies — you don’t need to handle a 404 or walk the fallback yourself. Only GET /organisation returns 404 when nothing is configured.
  • DELETE returns what now applies. A delete answers 200 with the response envelope rather than 204, so you don’t need a second request to see the fallback now in force.
  • PUT /…/leave replaces 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, as effective shows.

Authentication

Every request needs an X-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.