Skip to main content
The Pay API is the core of Flow Payroll. It covers the full payroll cycle: setting up your organisation and payroll configurations, managing employee records and their tax details, uploading pay data, locking pay runs, retrieving payslips, and submitting RTI (FPS and EPS) to HMRC. Use it when you integrate payroll processing itself — the other services in this reference (pensions, statutory pay, court orders, and so on) build on the data this API manages. The Pay API sits at the /v1 base path, directly under the gateway base URLs listed in the API reference introduction. All endpoint paths on this page are relative to that base path.

Key concepts

  • Payroll configuration — a pay schedule that employees are assigned to. Every pay run belongs to exactly one payroll configuration, and an organisation can run several in parallel.
  • Pay run — the processing of one pay period, identified by payroll configuration, tax year, and period. Locking a run finalises it.
  • Pay element — an organisation-level definition of a pay item type (for example basic pay or overtime) that uploaded pay data refers to.
  • Payroll line — a single pay item uploaded for an employee for a pay date. Payroll lines are the input to a pay run’s gross-to-net calculation.
  • Payslip — the per-employee output of a pay run, retrievable individually, by pay run, or across a date range.
  • Effective-dated records — tax codes, NI categories, working patterns, and SSP qualifying-day policies are stored with effective dates, so a change applies from a given date without losing history.
  • FPS — the Full Payment Submission sent to HMRC for a specific pay run.
  • EPS — the Employer Payment Summary for a tax month, submitted from monthly balances or manually.
  • Year-end finalisation — closes a tax year for a payroll configuration once its final pay run is complete.

API structure

Organisation

Manage organisation information, HMRC settings, opening balances, year-to-date running balances, and PAYE liability, including recording the PAYE payments you make to HMRC. GET /organisation/information and POST /organisation/hmrc/settings are the starting points; organisation-wide working-pattern defaults live under /organisation/working-pattern-defaults. See the organisation setup guide for the product-side view.

Pay elements

Create, read, update, and delete the pay elements available to your organisation under /organisation/pay-element. Payroll lines reference these definitions.

Payroll configurations

Create and manage pay schedules with POST /payrollconfig and its companions. A configuration can carry its own working-pattern defaults that override the organisation default. GET /payrollconfig/{payrollConfigId} returns hasPayRuns, which tells you whether the pay schedule is still editable before you offer the fields — see Pay schedule changes below. The pay run settings guide explains the equivalent product screens.

Employees

Full lifecycle management of employee records: POST /employees to create, GET /employees to list with pagination, plus lookups by National Insurance number and payroll ID. Working patterns, working schedules, and SSP qualifying-day policies are managed per employee as effective-dated records — see working patterns for how a pattern is resolved and what each kind means. See also managing people.

Tax codes, NI, and loans

Maintain each employee’s effective-dated tax codes and NI categories, update their NI number or payroll ID, and start or end student and postgraduate loans. GET /employees/{employeeId}/effective-tax-info returns the tax code and NI category in force on a given date.

Starters and leavers

Set starter details with PUT /employees/{employeeId}/starter, and set or cancel leaver details individually or in bulk via PUT /employees/leaver/bulk. See the onboarding and leavers guides.

Payroll runs and pay data

Upload pay data with POST /payroll/line/{employeeId}, attach supporting documents to lines, and set employee opening balances and payroll-config assignments. Create a run for a specific period, or use POST /payroll/{payrollConfigId}/next to create the next one in sequence, then finalise it with POST /payroll/{payrollConfigId}/{year}/{period}/lock. Listing endpoints return runs by tax year and the employees included in a run.

Calculator

POST /calculator/grosstonet calculates gross-to-net values, including tax and NI, so you can preview figures before locking a run.

Payslips

Retrieve payslips for a whole pay run, for an employee in a period, or for an employee across a date range. You can also set payslip metadata such as notes and a payment-date overwrite, and send payslip notification emails with POST /notification/email/payslip/{payrollConfigId}/{year}/{period}. See the payslips guide.

Reading pension on a payslip

A payslip reports pension in two places, and they describe the same money:
  • forPeriod.pension (and the year-to-date and employment-to-date equivalents) is the member’s total pension movement for the slice: contributions deducted, including additional voluntary contributions, less any opt-out refunds paid back.
  • summaryItems carries one row per pension contribution, each classified by pensionLineType, for organisations using itemised pension contributions.
Read one source or the other, never both. If you sum the deductions in summaryItems and then add forPeriod.pension on top, you will double count every pension contribution.
pensionLineType is the field to branch on. It is null on every non-pension item, so the presence of a contribution row is what tells you which source to use: A refund is money going back to the employee, so it is a payable row with a positive amount, and forPeriod.pension has it netted off already. Keep it out of your contribution total and subtract it separately if you need the member’s net movement for the period.
pensionRefundsByType on the same object breaks the period’s refunds down per scheme type, if you would rather read the totals than the rows. Two more fields ride on those rows and are never additive:
  • pensionEmployerContribution is the employer contribution attributable to that line. It is already inside forPeriod.employerPension, so it is display metadata, not a separate cost.
  • pensionMetadata records the context the row calculated under: the pension group, the contribution basis, the employer share, and a contributionSource saying whether the settings came from the group, a standing per-employee override, or an override for this pay run only.
Branch on pensionLineType, not on anything about the organisation. A payslip calculated before an organisation’s pension setup changed still has to add up, and the rows on the payslip are the only thing that tells you how it was calculated.
Salary sacrifice behaves the same on both engines: sacrificed pay is the employer’s contribution, so it stays out of forPeriod.pension (which is 0 for a purely sacrifice arrangement) and lands in forPeriod.employerPension. The sacrifice itself has always appeared in summaryItems as a Contribution row, because that is how a payslip shows it. What is new for relief-at-source and net-pay-arrangement schemes is that their contributions now appear as rows too, where previously forPeriod.pension was the only place they showed up.

RTI

Trigger an FPS for a pay run with POST /rti/fps/{payrollConfigId}/{taxYear}/{period} and retrieve its details. Submit an EPS from monthly balances or manually, resubmit a manual EPS, and list submissions by tax year. See the HMRC RTI guide.

Year end

Finalise year-end for a payroll configuration with POST /payroll/year-end/{taxYear}/{payrollConfigId}/finalise, and read finalisations per configuration or across the organisation.

Typical workflow

  1. Set your organisation information and HMRC settings with POST /organisation/information and POST /organisation/hmrc/settings.
  2. Create a payroll configuration with POST /payrollconfig and define your pay elements under /organisation/pay-element.
  3. Add employees with POST /employees, then set their starter details, tax codes, and NI categories.
  4. Upload each employee’s pay data for the period with POST /payroll/line/{employeeId}.
  5. Preview the figures with POST /calculator/grosstonet and resolve any validation issues reported on upload.
  6. Lock the run with POST /payroll/{payrollConfigId}/{year}/{period}/lock, then retrieve payslips for the run and email them to employees. The running a pay run guide covers the same flow in the product.
  7. Submit the FPS for the run with POST /rti/fps/{payrollConfigId}/{taxYear}/{period}, and submit the EPS for each tax month from monthly balances.

Notes and best practices

  • Pagination — GET /employees accepts pageSize (1–100) and paginationToken; pass the returned token to fetch the next page.
  • Payroll line uploads — each request accepts up to 100 lines. Lines that fail validation are reported back, and every accepted line’s response includes a payrollDate: the date you supplied, or the next unlocked pay date if that date’s run is already locked.
  • Locking — locking creates the pay run record in a locked state for that configuration, tax year, and period. Upload and correct pay data before you lock.
  • FPS request body — POST /rti/fps/{payrollConfigId}/{taxYear}/{period} takes { "finalSubmissionDetails": { ... }, "lateReason": "..." }, both optional. lateReason is HMRC’s late reporting reason for an FPS sent after the payment date (codes A, B, C, D, F, G, H; the withdrawn code E is rejected) and is reported on every payment. Sending the final submission details object as the body itself is deprecated as input and stops being accepted on 15 January 2027; wrap it in finalSubmissionDetails. See Deprecations.
  • EPS corrections — a manual EPS can be resubmitted with POST /rti/eps/{taxYear}/manual/{submissionId}/resubmit if HMRC rejects it or the figures change.
  • Opening balances — the flat NI totals on openingBalanceDirector and the aggregate pension on openingBalance are deprecated as input and stop being accepted on 7 November 2026; send niBandResult and pensionContributionsByType instead. Both keep being returned on responses, so only what you send changes. See Deprecations.
  • Salary — salary is deprecated as input on PUT and PATCH /employees/{employeeId}, and stops being accepted on those two operations on 20 December 2026; record changes with PUT /employees/{employeeId}/salary, which keeps an effective-dated history. POST /employees still accepts it as the starting salary, and it keeps being returned on every response as the amount in force today, so only what you send changes. See Deprecations.
  • Pay schedule changes — payFrequency and paymentPeriodConfig can only be changed on a payroll configuration that has no pay runs. Once a run exists, of any status, PUT /payrollconfig/{payrollConfigId} refuses a change to either with a 409 and the token payroll_config_has_pay_runs. Everything else on the configuration stays editable, and re-sending the two fields unchanged is always accepted, so a full-body PUT that only renames the schedule still works. GET /payrollconfig/{payrollConfigId} returns hasPayRuns so you can show the fields as locked instead of waiting for the refusal; it is read-only and ignored if you send it back.
  • Payslip PDFs — GET /payslip/data/{employeeId}/{year}/{period}/file returns the payslip as application/pdf (send Accept: application/pdf), with the file name in Content-Disposition. It is not password-protected; only the copy emailed to the employee is.
  • Pension totals: take pension from the summaryItems rows or from forPeriod.pension, never from both. See reading pension on a payslip.

Authentication

Every request needs a bearer token in the X-Auth-Token header, and credentials with access to more than one organisation should also send X-Org-Id — see Authentication. All responses use the shared envelope described in Response format.