Home Payroll API

Payroll API

Technical documentation for developers integrating UK payroll into their software via the Moonworkers API.
Nicolas Croix
By Nicolas Croix •
12 articles

Payroll API: Overview

The Moonworkers Payroll API is a REST API that lets software developers run complete UK payroll inside their own products: payroll calculation, payslips and documents, and HMRC submissions. The full reference is at https://payroll-api.moonworkers.co.uk/api/redoc/. What does the API cover? The whole payroll cycle, with nothing available only in the app. It calculates PAYE income tax, employee and employer National Insurance (including directors on the annual or alternative method), Construction Industry Scheme deductions, zero-hours and irregular-hours workers, and all statutory payments (SSP, SMP, SPP, SAP, ShPP, SPBP, neonatal care pay). See "Which parts of the payroll process does the API cover?". How are HMRC submissions made? Through Moonworkers' HMRC recognition, so you do not need your own. Your integration creates the FPS for each payrun and submits it, sends the EPS when there is something to report, and handles CIS300 returns and subcontractor verification. Notices HMRC sends back through the Data Provisioning Service (tax codes, student loans) arrive through the API too. See "Do we need our own HMRC recognition to file through the Payroll API?". Can one integration serve many employers? Yes. One set of credentials covers as many employers and PAYE schemes as you need, each with its own pay schedules running at once. This is built for bureaux, accountancy platforms and workforce tools. See "Is the Payroll API built for multi-tenant or bureau use?". Which documents does it produce? Payslips, P45, P60, P11D, P30, P32 and CIS statements, as PDFs through the API. How do I get access? The sandbox is free, with no approval step: request a key at https://www.moonworkers.co.uk/api and the credentials arrive by email. See "How do I get a Payroll API sandbox key?" and "Authenticating with the Payroll API". How is it priced? Per payslip, on volume bands, with no minimum commitment. See "Payroll API pricing: per payslip, not per employee". What service level applies? Production accounts are covered by a 99.9% uptime SLA. Enterprise terms are available on request.

Last updated on Sep 19, 2026

How do I get a Payroll API sandbox key?

You can have one today without talking to anyone. How do I request it? Request a key at https://www.moonworkers.co.uk/api. The credentials arrive by email: an API key and a client secret, sent together on every request as the X-API-Key and X-Client-Secret headers. See "Authenticating with the Payroll API". Which URL do I call? The sandbox has its own base URL: https://payroll-api-sandbox.moonworkers.co.uk. Production is https://payroll-api.moonworkers.co.uk. Every path ends in a trailing slash, for example /api/employer/. What does the sandbox include? The full API, with payroll calculations and documents exactly as in production, on test data. Nothing reaches HMRC's live service: RTI submissions run in HMRC test mode. Sandbox use is free, with no commitment and no approval step. Keep in mind that we reset sandbox data periodically, and that rate limits are lower than in production. If you plan to load test, tell us first. Can I try calls in the browser? Yes. The Swagger UI on the sandbox, at https://payroll-api-sandbox.moonworkers.co.uk/api/docs/, lets you run any endpoint with your key against the sandbox. I lost my client secret, or it was exposed The secret is shown only once, in the credentials email. Contact us and we will revoke the pair and issue a new one. Treat the secret like a password: never commit it to source control or paste it into a ticket or a chat, including this one. Can I see my sandbox data in a user interface? Yes. The sandbox front end is at https://payroll-sandbox.moonworkers.co.uk/auth/login. It shows the same data as the API, which helps when you want to check what your integration has created. Where do I start? Read the API reference at https://payroll-api.moonworkers.co.uk/api/redoc/, then create an employer, a pay schedule and an employee, and run a first payroll. "Payroll API resource map" lists what each part of the API does. To test HMRC submissions, see "Can we test HMRC submissions and DPS notices in the sandbox?".

Last updated on Sep 19, 2026

Can we test HMRC submissions and DPS notices in the sandbox?

Yes for both, in different ways. How do I test FPS and EPS? You do not need Government Gateway credentials of your own. Switch is_test_mode_enabled on in the employer's RTI submission settings, and submissions go to HMRC's test service carrying our test credentials. That takes FPS and EPS end to end with no Gateway set-up on your side. You register your own credentials only when the employer moves to production; gov_gateway_credentials_set on those settings confirms they were registered. Remember to switch test mode off for a live employer. With it on, real submissions do not reach HMRC's live service. How do I test DPS notices? HMRC sends notices through its Data Provisioning Service, and as far as we know there is no test channel that delivers one, so a notice cannot arrive in the sandbox the way a real one does. Instead, we place test notices directly on your sandbox employer, on request. Contact us with the sandbox employer and the notice types you want (P6, P9, SL1, SL2, PGL1). You can then test everything your integration touches: GET .../dps/ to list notices, GET .../dps/{id}/ to read one, GET .../dps/by-sequence/{sequence_number}/ to look one up, and POST .../dps/{id}/apply/ to apply it to the employee. Applying sets a new tax code for a P6 or P9, or starts or stops a loan for SL1, SL2 or PGL1, from the next payrun calculated for that employee. Applying is refused if the notice has already been applied or arrived with errors, so you can test the failure paths too. Notices are read-only over the API, which is why seeding has to come from us. Can I see the results in a user interface? Yes, the sandbox front end is at https://payroll-sandbox.moonworkers.co.uk/auth/login.

Last updated on Sep 19, 2026

Authenticating with the Payroll API

Every request to the Payroll API is authenticated. This article covers the two methods, the conventions every request follows, and the shape of an error. It is generated from the API reference, version 0.5.0. How do I authenticate? There are two ways to authenticate, and which one you want depends on what you are building. API key: for server-to-server integrations. Send both headers on every request: curl "https://payroll-api.moonworkers.co.uk/api/employer/" \ -H "X-API-Key: YOUR_API_KEY" \ -H "X-Client-Secret: YOUR_CLIENT_SECRET" import requests headers = { 'X-API-Key': 'YOUR_API_KEY', 'X-Client-Secret': 'YOUR_CLIENT_SECRET', } response = requests.get('https://payroll-api.moonworkers.co.uk/api/employer/', headers=headers) const response = await fetch('https://payroll-api.moonworkers.co.uk/api/employer/', { headers: { 'X-API-Key': 'YOUR_API_KEY', 'X-Client-Secret': 'YOUR_CLIENT_SECRET', }, }) Bearer token: used by the Moonworkers web app. POST /api/auth/login/ returns an access_token; send it as a bearer token, and refresh it through POST /api/auth/token/refresh/ when it expires: curl "https://payroll-api.moonworkers.co.uk/api/employer/" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" The authentication endpoints themselves: login, registration, activation, password reset and the social OAuth flow: exist for the web app. If you are building an integration, use an API key rather than logging in as a user. A handful of endpoints take a bearer token only, and refuse an API key with TokenAuthRequiredException (403). They act on behalf of the signed-in person rather than on the account: their own profile, password and two-factor settings, the notifications addressed to them, the team roster they administer, and the billing calls that hand a human a Stripe checkout page. An API key names an account, not a person, so on these routes it has no one to act as. Each one says so at the top of its description, and lists only the bearer scheme under its padlock. Which base URL do I use? The sandbox is at https://payroll-api-sandbox.moonworkers.co.uk and production at https://payroll-api.moonworkers.co.uk. The examples below use production; swap the host while you build. The sandbox also has a Swagger UI at https://payroll-api-sandbox.moonworkers.co.uk/api/docs/ to try calls in the browser. How are requests shaped? Most resources hang off an employer: /api/employer/{employer_pk}/..., and reports are nested the same way: employer-level ones such as the P30 and P32 under /api/employer/{employer_pk}/report/..., and per-employee documents such as the P45, P60 and P11D under /api/employer/{employer_pk}/employee/{employee_pk}/report/.... Every path ends in a trailing slash. Without it a GET redirects and a POST fails. Updates use PUT and replace the whole record. PATCH is not supported, so send every field you want to keep, not just the ones you are changing. How do errors come back? A failed request returns the error class name and a message: {"errorCode": "PayrunNotEditableException", "errorMessage": "This payrun is finalized."} Branch on errorCode rather than on the message text: messages may be reworded, codes will not. Authentication failures are the exception: they come from the framework itself and return {"detail": "..."}. The full list of error codes is in the "Payroll API error codes" articles. Get started The full reference is at https://payroll-api.moonworkers.co.uk/api/redoc/. No key yet? See "How do I get a Payroll API sandbox key?".

Last updated on Sep 19, 2026

Payroll API resource map

The Payroll API has 364 operations across 73 resources (reference version 0.5.0). This map groups them by task, says in one line what each resource is for, and links to its section of the reference. Almost every resource sits under an employer, at /api/employer/{employer_pk}/... Employers and set-up Employer (19 operations). Represents an employer entity in the payroll system. Employer (reports and settings) (17 operations). Includes: List employer addresses; Create employer address; Get an employer address; Update employer address; Delete employer address; Set default address; and more. Employer Tax Period (3 operations). Represents an employer's tax period for PAYE and NI reporting. Pay Schedule (10 operations). Defines the payment schedule for an employer within a tax year. Pay Code (6 operations). Represents a type of pay or deduction in the payroll system. Bureau (2 operations). Manage bureau information for customer entities that are bureaus. Onboarding (1 operation). Complete customer entity onboarding by updating entity information and marking onboarding as complete. Access Level (5 operations). Manage entity access levels for users within a customer entity. Pending Access (2 operations). Manage pending entity access invitations. Employees Employee (22 operations). Represents an individual employee within an employer's payroll. Employee Draft (3 operations). Temporary draft version of an employee record used during employee creation or import. Employee Drafts (4 operations). Includes: Create a new employee draft; Update an existing employee draft; Publish a draft to create an employee; Bulk delete employee drafts. Working Pattern (7 operations). Defines an employee's weekly work schedule including days worked, hours per day, and total weekly hours. Opening Balances (2 operations). Stores year-to-date payroll figures from previous employers or tax years for employees joining mid-year. NIC Summary (5 operations). Records National Insurance contribution summaries for employees by NI letter (A, B, C, etc.) and tax year. Benefits (15 operations). Represents employee benefits such as company cars, loans, living accommodation, relocation expenses, and mileage allowances. Custom Pay lines (5 operations). Allows creation of custom pay or deduction lines for employees outside of regular payrun calculations. Payroll runs and payslips Payrun (17 operations). Represents a single payroll run within a pay schedule for a specific period. Payslip (9 operations). Individual payslip for an employee within a payrun. Payline (6 operations). Single line item on a payslip representing a specific type of pay or deduction. Payrun Journal (5 operations). Accounting journal entry generated from a finalized payrun. Payrun Payment (7 operations). Represents a batch payment for a payrun sent to a payment processor (Modulr, Wise, etc.). Payrun Payment Item (5 operations). Individual employee payment within a PayrunPayment batch. Pay Period Report (2 operations). Summary report for a pay period showing payroll statistics including employee counts, gross pay, cost to employer, statutory pay totals, and demographic breakdowns. Leave and statutory pay Leave Record (6 operations). Represents a single leave request or leave period for an employee. Leave Allowance (2 operations). Tracks an employee's leave entitlement and usage. Leave Calendar (1 operation). Provides a calendar view of all leave records for employees within a specified date range. Irregular and zero-hours workers Irregular Work Rates (6 operations). Defines hourly or daily pay rates for irregular employees (zero-hours, casual workers). Irregular Work Assignments (7 operations). Represents a work assignment for an irregular employee with specific start/end times and pay rate. Irregular Pay Week (7 operations). Aggregates irregular work assignments for an employee within a calendar week. Pensions and auto-enrolment Pension Provider (7 operations). Represents a pension provider organization connected to an employer. Pension Scheme (6 operations). Defines a pension scheme under a pension provider. Pension Worker Group (6 operations). Defines contribution rules and thresholds for a group of employees within a pension scheme. Auto-enrolment Assessment (11 operations). Assesses an employee's eligibility for automatic pension enrolment based on age, earnings, and employment status. Pension Enrolment (6 operations). Records an employee's enrolment into a pension scheme via a worker group. Pension Contributions Submission (4 operations). Represents a submission of pension contributions to an external pension provider. HMRC submissions and notices FPS Submission (8 operations). Full Payment Submission to HMRC via Real Time Information (RTI). FPS Correction (6 operations). Correction submission for a previously submitted Full Payment Submission (FPS). EPS Submission (6 operations). Employer Payment Summary submission to HMRC. EXB Submission (6 operations). Expenses and Benefits (EXB) submission to HMRC. NVR Submission (6 operations). National Insurance Number Verification Request submission to HMRC. DPS (5 operations). Notices HMRC sends to the employer through the Data Provisioning Service: tax code changes (P6, P9), student and postgraduate loan starts and stops (SL1, SL2, PGL1, PGL2), and CIS messages. Apprenticeship Levy (1 operation). Tracks apprenticeship levy calculations and submissions for employers with annual pay bill over £3 million. Construction Industry Scheme CIS Verification (6 operations). Construction Industry Scheme verification request to HMRC. CIS300 Submission (7 operations). CIS300 monthly return submission to HMRC. CIS Statement (2 operations). Construction Industry Scheme statement showing CIS deductions made from subcontractors. Statutory documents and reports P11 (2 operations). HMRC form P11 - Deductions Working Sheet. P11 Detailed (2 operations). Detailed version of P11 form showing month-by-month breakdown of pay, tax, and NI deductions. P11D (2 operations). HMRC form P11D - Expenses and Benefits. P30 (1 operation). HMRC form P30 - Employer Payment Summary. P32 (1 operation). HMRC form P32 - Employer Payment Record. P45 (2 operations). HMRC form P45 - Details of employee leaving employment. P60 (2 operations). HMRC form P60 - End of Year Certificate. Integrations and events External Provider (12 operations). Represents an external service provider integration (accounting software, pension providers, payment processors). Webhook (10 operations). Represents a webhook configuration for an employer to receive event notifications. User Notification (4 operations). System notification sent to users about asynchronous events or status updates. What about sign-in, profiles and billing? Those endpoints serve the Moonworkers web app and act on behalf of a signed-in person. An integration authenticates with an API key and does not need them. See "Authenticating with the Payroll API".

Last updated on Sep 19, 2026

Payroll API error codes: Accounting to Pay schedule

A failed request returns an errorCode and an errorMessage. Branch on errorCode, which never changes, rather than on the message text. Authentication failures are the exception and return {"detail": "..."}. This is part 1 of 2 of the list, generated from the API reference, version 0.5.0. It covers: Accounting, Benefits, CIS, Core, Employee, Employer, General, Integrations, Irregular workers, Leave, Opening balances, Pay schedule. Accounting Code Default message Raised when MissingCodeMappingException Exception raised when pay codes are not mapped to nominal codes for an external accounting provider. PayrunJournalNotBalancedException Exception raised when a payrun journal's debits don't equal credits. Accounting journals must balance (total debits = total credits). PayrunJournalNotDeletableException Exception raised when attempting to delete a payrun journal that cannot be deleted. Benefits Code Default message Raised when BenefitConfigurationException Benefit configuration error Exception raised when benefit configuration is invalid. Used for validation errors such as missing required fields, invalid benefit types, or incompatible settings. BenefitNotDeletableException Benefit is in use and cannot be deleted Exception raised when attempting to delete a benefit that is in use. Benefits that have been included in finalized payruns or P11D forms cannot be deleted. CompanyAssetNotDeletableException This company asset is linked to a benefit and cannot be deleted Raised when deleting a company car or van asset that is still linked to a benefit. CIS Code Default message Raised when CISExcludedException This feature is not available to CIS subcontractors Exception raised when attempting to use a feature that is not available to CIS subcontractors. CIS subcontractors have restrictions on certain features (e.g., benefits). CISOnlyException This feature is exclusive to CIS subcontractors Exception raised when attempting to use a feature that is exclusive to CIS subcontractors. Some features are only available to CIS subcontractors (e.g., CIS statements). IrregularsOnlyException This feature is exclusive to irregulars Exception raised when attempting to use a feature that is exclusive to irregular workers. Some features are only available to irregular workers (e.g., irregular work assignments). Core Code Default message Raised when AddressNotDeletableException This address is not deletable Exception raised when attempting to delete a default address. Default addresses cannot be deleted to ensure entities always have at least one address. UnsupportedPayPeriodException This pay period has no corresponding pay frequency Exception raised when a PayPeriod has no corresponding PayFrequency code. Daily (and any future period without an RTI frequency mapping) cannot be converted for FPS/PAPDIS output. Employee Code Default message Raised when EmployeeHasLeftException This employee has left Exception raised when attempting to perform an operation on an employee who has already left the company. EmployeeHasNotStartedException This employee has not started yet Exception raised when attempting to perform an operation on an employee who has not yet started their employment. EmployeeHoldException Exception raised when attempting to place an employee on hold or release from hold when the operation is not valid (e.g., already on hold, not on hold). EmployeeInactiveException This employee is inactive Exception raised when attempting to perform an operation on an inactive (former) employee. EmployeeNotDeletableException This employee cannot be deleted Exception raised when attempting to delete an employee that cannot be deleted (e.g., has finalized payslips). MinimumWageNotMetException Exception raised when an employee's pay does not meet the minimum wage requirements. NILetterException Exception raised when there is an issue with National Insurance letter processing. NINORequiredException Employee's NINO is not defined Exception raised when an operation requires the employee's National Insurance number but it is not defined (e.g. verifying the NINO with HMRC). OffPayrollWorkersExcludedException This feature is not available to off-payroll workers Exception raised when attempting to use a feature that is not available to off-payroll workers. TaxCodeException A Tax code error occurred Exception raised when there is an error with tax code validation or processing. Employer Code Default message Raised when ApprenticeLevyNotEnabledException Apprenticeship Levy is not enabled for this employer Exception raised when attempting to use apprenticeship levy features when apprenticeship levy is not enabled for the employer. EmploymentAllowanceMaxClaimExceededException Employment Allowance amount exceeds the HMRC annual maximum Exception raised when the requested Employment Allowance amount exceeds the HMRC annual maximum for the tax year. InvalidTaxYearDataException Invalid or incomplete tax year data Exception raised when a tax year directory cannot be loaded (missing or incomplete base.json, or no 'tax_year' key). Fatal on purpose: bad tax-year data must stop startup rather than silently drop a year from available_tax_years. TaxYearNotAvailableException This tax year is not available for the employer Exception raised when attempting to access a tax year that is not available for the employer. TaxYearNotEditableException This tax year is not editable Exception raised when attempting to edit data for a tax year that is not editable (e.g., not the current tax year). YearEndReachedException This tax year has ended Exception raised when attempting to perform operations after the tax year has ended. General Code Default message Raised when AccountActivationException Account activation failed Raised when an account activation link is malformed or does not match a user AdminPermissionRequiredException Raised when a non-admin user tries to perform an admin-only action BureauOnlyException CannotDeleteLastAdminException Raised when trying to delete the last admin user CannotDowngradeLastAdminException Raised when trying to downgrade the last admin user CaptchaVerificationException Captcha verification failed Raised when the Cloudflare Turnstile token sent with an anonymous request is missing, invalid, expired, already used, or could not be checked. CustomNotAuthenticated DuplicateMemberException Raised when trying to create an access level for an email that already has access EmailAlreadyInvitedException Raised when trying to invite an email that already has a pending invitation EmployerQuotaExceededException FeatureNotAvailableException This feature is not currently available Raised when a feature is not currently available (e.g. not yet implemented or disabled), so the caller gets a clear message instead of an unhandled error. Wrap in BadRequest. InactiveMembershipSubscriptionException The subscription is not active. Raised when an operation requires an active subscription but it isn't active (e.g. cancelling an already-inactive one). A trivial user/state error, not a payment-platform failure: surfaced as a 400, not worth logging. MalformedURLException The request URL is malformed. Raised when a view is reached without a URL kwarg its route is supposed to supply (e.g. employer_pk, tax_year). This is a routing/configuration error, never a client mistake: hence a 500: but it still returns the structured error envelope. MembershipRequiredException OnboardingAlreadyCompletedException Onboarding has already been completed for this account Raised when onboarding is re-submitted for a customer entity that has already completed it. Onboarding creates an Employer (with ownership) or a Bureau, so replays must be rejected rather than minting duplicates. Wrap in BadRequest. PaymentPlatformException The payment platform could not complete the request. Please try again later. Raised when a call to the payment platform (Stripe) fails, so the error is surfaced to the user instead of leaking as a 500. PaymentRequiredException SocialAccountConflictException This email is already registered with a different sign-in method Raised when a social login resolves an email that already belongs to a local (password) account or an account linked to a different provider. Social and local identities never merge, so we refuse rather than link. TokenAuthRequiredException This endpoint requires a user access token. API key credentials are not accepted here. Raised when a request carrying API key credentials reaches an endpoint that acts on behalf of a signed-in person: account settings, team membership, billing. The credentials are valid; they are simply the wrong kind for this route, which is why this is a 403 and not a 401. A 401 would invite the client to refresh and retry forever. UserAlreadyExistsException An account already exists for this email address Raised when an account is requested for an email that already has one. Integrations Code Default message Raised when CSVParsingException Unable to parse the CSV file Raised when an imported CSV file cannot be parsed (malformed rows, bad column mapping, empty file). CompanyNotFoundException Company not found on provider Raised when a tenant/company lookup on the provider returns a non-success response (e.g. wrong tenant_id). ContributionsAlreadyQueuedException A contributions file for this scheme is still being processed by the provider Raised when a contributions file for the same scheme is still in flight. The People's Pension accepts one file per account at a time and rejects the next one with "A file is already queued for processing" until the previous file reaches the processed state. ExternalProviderNotConnectedException Raised when the provider is not connected or credentials have expired. ExternalProviderOperationException Raised when an API operation fails. Includes HTTP status and response body. IntegrationProviderException Base exception for integration-related errors. MissingContributionDataException Missing data required by the pension provider Raised when an employee in a contributions submission is missing data the provider requires. PayReferencePeriodMismatchException The submitted period does not match the provider's next pay reference period Raised when a contributions period does not match the one the provider expects next. The People's Pension validates the submitted payReferencePeriod against the nextPayReferencePeriod it advertises on the account, and rejects the file when they differ. Failing here surfaces an actionable message instead of a provider validation failure hours later. Irregular workers Code Default message Raised when AssignmentOverlapException Some assignments are overlapping Exception raised when assignments overlap with each other. AssignmentOverlapsLeaveException Assignment cannot overlap with an existing leave Exception raised when an assignment overlaps with an existing leave period. IrregularWorkAssignmentInvalidDatesException Assignment end date must be after start date Exception raised when assignment dates are invalid (e.g., end before start, outside boundaries). IrregularWorkAssignmentNotDeletableException Irregular work assignment cannot be deleted Exception raised when attempting to delete an assignment in a closed pay period. IrregularWorkerRateDuplicateNameException A rate with this name already exists for this employee Exception raised when attempting to create a rate with a name that already exists for the employee. IrregularWorkerRateNotDeletableException Cannot delete default rate Exception raised when attempting to delete a default rate. PayPeriodClosedException A pay period is closed Exception raised when attempting to modify assignments in a closed pay period. Leave Code Default message Raised when EmployeeNotLeaverException Employee is not leaver Raised when an operation requires an employee to be a leaver but they are not. EntitlementBasisNotEditableException Holiday entitlement basis cannot be changed when employee has leave history or remnant allowance from previous years Raised when attempting to change holiday entitlement basis for employee with leave history or remnant allowance. InvalidHolidayEntitlementBasisException Irregular workers must use HOURS_PER_WEEK as holiday entitlement basis Raised when irregular employee uses incorrect holiday entitlement basis (must be HOURS_PER_WEEK). InvalidLeaveDatesException An error occurred Raised when leave dates are invalid (e.g., leave_to <= leave_from, before start date, after leave date). InvalidLeaveFieldException An error occurred Raised when a leave field is missing or incompatible with the leave type (partial-day times, baby date, neonatal care payment tier / start / end dates). InvalidLeavePayException An error occurred Raised when leave pay type is incompatible with leave type (e.g., DO_NOT_PAY for holiday, STATUTORY_PAY for unauthorized). InvalidResignationDateException Raised when resignation date is invalid (e.g., before last finalized payslip end date). LeaveAllowanceExceededException An error occurred Raised when attempting to use more leave allowance than available. LeaveNotDeletableException An error occurred Raised when attempting to delete a leave that overlaps with finalized payruns. LeaveNotEditableException An error occurred Raised when attempting to edit a leave that overlaps with finalized payruns. LeaveOverlapException An error occurred Raised when a leave overlaps with another existing leave for the same employee. LeavePreviewNotAvailableException Leave preview is not available: the employee has no working pattern Raised when a leave preview cannot be computed (e.g. the employee has no working pattern). OverrideHoursTotalException Holiday pay for casual workers require total hours information Raised when override_hours_total is missing or incorrectly set for irregular employees. SSPWeekBreakdownException SSP week breakdown is required and must match the number of administrative weeks in the leave period Raised when SSP week breakdown is missing or has incorrect length for irregular employees. StatutoryPayRequirementException An error occurred Raised when statutory pay requirements are not met (e.g., insufficient earnings, employment duration, etc.). Opening balances Code Default message Raised when DuplicateNILetterException A NIC summary for this NI letter already exists in this tax year Raised when a NIC summary would violate the unique (employee, ni_letter, tax_year) constraint. OpeningBalancesNotAvailableForTaxYearException An error occurred Exception raised when opening balances cannot be set for a tax year. Typically occurs when employee's start date is on or before the tax year start date, meaning there are no previous periods to carry forward balances from. Pay schedule Code Default message Raised when PayScheduleConfigurationException Exception raised when pay schedule configuration is invalid. Used for validation errors such as mismatched tax years, invalid dates, or invalid day numbers for monthly schedules. PayScheduleNotDeletableException A payrun already started for this tax year Exception raised when attempting to delete a pay schedule that has payruns or is the only default schedule for a period. PayScheduleNotEditableException A payrun already started for this tax year Exception raised when attempting to edit a pay schedule that has payruns. Once payruns exist, certain fields (dates, period, tax_year) cannot be changed.

Last updated on Sep 19, 2026

Payroll API error codes: Payrun to Working patterns

A failed request returns an errorCode and an errorMessage. Branch on errorCode, which never changes, rather than on the message text. Authentication failures are the exception and return {"detail": "..."}. This is part 2 of 2 of the list, generated from the API reference, version 0.5.0. It covers: Payrun, Pensions, RTI, Statutory documents, Webhooks, Working patterns. Payrun Code Default message Raised when BankDetailsRequiredException Raised when attempting to create a payment for an employee without bank details. PayCodeAlreadyExistsException A pay code with this code already exists Raised when creating a custom pay code whose code collides with a system code or another of the employer's own codes (the two share a lookup namespace). PayCodeNotDeletableException Pay code cannot be deleted Raised when attempting to delete a system/control/employer pay code or one that is in use. PayCodeNotEditableException Pay code cannot be edited Raised when attempting to edit a system pay code or a pay code that is in use. PayLineHoursNotAllowedException Hours can only be set on the basic pay line of a regular hourly-paid employee Raised when setting period hours on a payline that doesn't support it (not the basic pay line, or an employee who isn't a regular hourly-paid worker). PaymentAlreadyExistsException This payrun already has a payment Raised when a payment already exists for the payrun. The create serializer rejects this up-front; this covers two concurrent creates racing past that check onto the unique constraint. PaymentAlreadySubmittedException Payment has already been submitted Raised when attempting to submit a payment that has already been submitted. PaymentNotDeletableException Payment cannot be deleted due do its status Raised when attempting to modify a payment that is not in PENDING status. PaymentNotEditableException Payment cannot be modified when not in PENDING status Raised when attempting to modify a payment that is not in PENDING status. PaymentNotFoundException Payment not found Raised when a referenced payment batch does not exist for the employer. PaymentNotResetableException Payment cannot be reset PaymentProcessorNotAvailableException Payment processor not available Raised when no payment processor is available for the requested processor type. PaymentSubmissionFailedException Payment submission failed Raised when submitting a payment batch to the processor fails. PayrunAlreadyStartedException A payrun already started for this tax year Raised when attempting to start a payrun but one already exists for the tax year. PayrunInProgressException The current payrun for this pay schedule has not been finalized yet Raised when attempting an operation that requires a finalized payrun but current one is not finalized. PayrunNotDeletableException Payrun cannot be deleted Raised when attempting to delete a payrun that is not the current payrun. PayrunNotEditableException Payrun cannot be modified Raised when attempting to modify a finalized payrun. PayrunNotFinalizableException Payrun cannot be finalized Raised when attempting to finalize a payrun that is not in finalizable state. PayrunNotReopenableException Payrun cannot be reopened Raised when attempting to reopen a payrun that cannot be reopened (not last payrun or not finalized). Pensions Code Default message Raised when AssessmentExistsException This employee has already been assessed for auto-enrolment Exception raised when attempting to perform an assessment on an employee who has already been assessed. AssessmentNotPostponedException This employee's assessment has not been postponed Exception raised when attempting to perform operations that require a postponed assessment, but the assessment has not been postponed. AutoEnrolmentBeforeStagingDateException Auto-enrolment cannot be dated before the employer's staging date Exception raised when an auto-enrolment is dated before the employer's staging date. The employer has no auto-enrolment duties until its staging date, so nothing can be auto-enroled before it. Voluntary (non auto-enrolment) joiners are not bound by it. AutoEnrolmentExistsException This employee is already auto-enroled Exception raised when attempting to auto-enrol an employee who is already auto-enrolled. ConnectedPensionProviderNotFoundException No connected integration was found for this pension provider Exception raised when no connected external integration exists for the requested pension provider, so it cannot be synchronized or connected. DuplicateWorkerGroupEnrolmentException Employee is already enroled in this worker group Exception raised when attempting to enrol an employee in a worker group they are already enrolled in. EmployeeExemptedFromAutoEnrolmentException This employee is currently exempted from auto-enrolment Exception raised when attempting to perform auto-enrolment operations on an employee who is exempted from auto-enrolment. EmployeeNotAssessedException This employee has not been assessed for auto-enrolment yet Exception raised when attempting to enrol an employee who has not been assessed for auto-enrolment eligibility. EnrolmentNotOptedOutException This enrolment is not opted out Exception raised when a refund is attempted on an enrolment that has not been opted out. EnrolmentNotRefundableException Only auto-enrolments can be refunded Exception raised when a refund is attempted on an enrolment that is not an auto-enrolment. Only auto-enrolment contributions can be refunded. ExternalOptOutsNotSupportedException This pension provider does not publish opt-outs made directly with it Exception raised when opt-outs are pulled from a pension provider that does not publish the ones members made directly with it. InactiveEnrolmentException This pension enrolment is inactive Exception raised when attempting to perform operations on an inactive enrolment. Enrolments that have been left/opted out cannot be modified. PensionProviderNotDeletableException Pension provider is in use and cannot be deleted Exception raised when attempting to delete a pension provider that is in use. Pension providers with active schemes/enrolments cannot be deleted. PensionProviderNotEditableException Pension provider is in use and cannot be modified Exception raised when attempting to edit a pension provider that is in use. Pension providers with active schemes/enrolments cannot be modified. PensionSchemeConfigurationException Exception raised when pension scheme configuration is invalid. Used for validation errors such as missing required fields or incompatible settings. PensionSchemeNotDeletableException Pension scheme is in use and cannot be deleted Exception raised when attempting to delete a pension scheme that is in use. Pension schemes with active enrolments cannot be deleted. PensionSchemeNotEditableException Pension scheme is in use and cannot be modified Exception raised when attempting to edit a pension scheme that is in use. Pension schemes with active enrolments cannot be modified. PensionSchemeNotQualifying Pension scheme is not qualifying for auto-enrolment Exception raised when attempting to use a non-qualifying pension scheme for auto-enrolment purposes. Only qualifying schemes can be used for automatic enrolment. PostponementMaxLimitReachedException Auto-enrolment assessment cannot be postponed for more than 3 months after start date Exception raised when attempting to postpone an assessment beyond the 3-month maximum limit from employment start date. SubmissionNotDeletableException This submission can no longer be deleted Exception raised when deleting a contributions submission that is in-flight at the provider (SENT, with a poll queued against it) or already accepted (SUCCESS). SubmissionNotPollableException Polling is only available for submissions that have been sent or failed Exception raised when polling is requested for a submission that has not been sent or failed, so there is no external status to check. SubmissionPayrunNotResolvableException No single finalized payrun matches this submission's period Exception raised when a contributions submission cannot be tied to exactly one finalized payrun for its period (none found, or several across schedules match). WorkerGroupNotDeletableException Worker group is in use and cannot be deleted Exception raised when attempting to delete a worker group that is in use. Worker groups with active enrolments cannot be deleted. WorkerGroupNotEditableException Worker group is in use and cannot be modified Exception raised when attempting to edit a worker group that is in use. Worker groups with active enrolments cannot be modified. WorkerGroupRequiredException NEST requires a worker group to be specified Exception raised when a NEST contributions submission is built without the worker groups NEST requires. RTI Code Default message Raised when RTICredentialsCheckException Could not verify your RTI credentials with HMRC. Exception raised when the RTI credentials check could not be completed (e.g. HMRC gateway unreachable or an unreadable response), as opposed to the credentials simply being rejected. RTICredentialsException Your RTI credentials were rejected by HMRC. Exception raised when the RTI credentials were checked successfully but HMRC rejected them as invalid (wrong Government Gateway user id / password). RTICredentialsRequiredException RTI Credentials are required to perform this action. Exception raised when RTI credentials are required but not available. RTIDocumentSignatureException Could not generate the document's IRmark signature. Raised when the IRmark (the document's integrity signature) cannot be generated. Submitting an unsigned document guarantees an HMRC rejection, so the failure must surface rather than silently produce unsigned XML. RTISubmissionNotDeletable This submission can no longer be deleted. Raised when deleting a submission HMRC has already accepted (SUCCESS). RTISubmissionNotEditable This submission can no longer be edited. Raised when editing a submission that is no longer editable (SENT/SUCCESS). RTISubmissionNotSubmittable This submission cannot be submitted. Raised when submitting a submission that cannot be sent (already SENT/SUCCESS, or XML not built). Statutory documents Code Default message Raised when InvalidTaxPeriodException Invalid tax period Exception raised when a tax period number is invalid (not between 1 and 12). NoEmployeeDataInTaxYearException There is no data for this employee in tax year Exception raised when attempting to generate a document for an employee who has no data in the specified tax year. Webhooks Code Default message Raised when WebhookURLAlreadyExistsException A webhook with this URL already exists for this employer. Raised when creating or updating a webhook to a URL that already exists for the employer. WebhookURLNotAllowedException This webhook URL is not allowed. Raised when a webhook URL targets a non-public address (SSRF guard). Covers a disallowed scheme, an unresolvable host, or a host that resolves to a loopback/link-local/private/reserved address: e.g. the cloud metadata endpoint or an internal service. Working patterns Code Default message Raised when InvalidScheduleException The provided schedule is invalid Exception raised when a working pattern schedule is invalid. Used when schedule data doesn't match expected structure or validation fails. WorkingPatternEmployerMismatchException Working pattern employer should not change Exception raised when a substitute working pattern belongs to a different employer. WorkingPatternNotDeletableException This working pattern is in use and cannot be deleted Exception raised when attempting to delete a working pattern that is in use or is the default pattern. WorkingPatternNotEditableException This working pattern cannot be modified Exception raised when attempting to edit a working pattern that cannot be modified. WorkingPatternNotWeekBasedException This working pattern does not use week days Exception raised when a week-day operation is attempted on a looping (non week-day) working pattern.

Last updated on Sep 19, 2026