The Problem: Coding Notices Are Small Messages With Big Payroll Impact
HMRC coding notices look deceptively simple. A new tax code, a student loan start instruction, or a postgraduate loan stop notice might arrive as just another message in a feed, but each one changes how payroll should run.
Before this change, retrieval and application were separate steps. Notices could be fetched into Workmax, but payroll teams still had to review the queue and apply them manually. That gave teams control, but it also created a bottleneck:
- HMRC sends the notice.
- Workmax stores it as received.
- Someone has to remember to review it.
- Someone has to apply it before the next payroll run.
That workflow is safe, but it is also easy to delay. In a compliance-heavy domain, delayed action is its own kind of risk.
The Goal: Automate the Right Notices, Keep Humans in the Loop
The key design decision was not to auto-apply everything.
Instead, we split coding notices into two groups:
- Actionable notices that directly change payroll configuration and can be applied safely by the system.
- Informational notices that should still be retrieved and surfaced, but left for human review.
In the current implementation, Workmax auto-applies:
P6P6BP9SL1SL2PGL1PGL2
General and informational notice streams such as NOT, RTI, and annual reminder-style items are still retrieved, but they remain in a received state for manual review.
That distinction matters. It lets us remove repetitive admin work without pretending every HMRC message should be handled the same way.
The Architecture: Retrieval, Persistence, Application, Notification
This feature spans more than one layer of the stack.
On the backend, the retrieval flow in retrieve-coding-notices.ts authenticates with HMRC DPS, requests multiple notice streams, parses the responses, updates fetch positions, and persists the retrieved notices. If the company has enabled autoApplyCodingNotices, the same flow then loads active employees and passes the notices into the automation pipeline.
At a high level, the orchestration now looks like this:
const codingNotices = await persistRetrievedCodingNotices(retrievedNotices);
if (hmrcSettings?.codingNoticeSettings?.autoApplyCodingNotices) {
const output = await PersonRepo.getPeopleByCompanyId(claims.companyId, PersonStatus.Active);
const people = output.Items?.length ? (output.Items as Person[]) : [];
await applyCodingNoticesWithSideEffects(claims.companyId, codingNotices, people);
}
That flow is supported by a dedicated repository layer in coding-notice.repo.ts, which handles batch reads, writes, deletes, and transactional updates in DynamoDB. Pulling this persistence logic behind a CodingNoticeRepo makes the automation code easier to reason about and test, and it gives us a single place to enforce how coding notices are stored and updated.
The Safety Rails: Automation With Explicit Limits
Auto-applying payroll changes only works if the automation is deliberately conservative.
1. Only actionable notices are applied
The automation layer uses an explicit whitelist rather than a loose default. If a notice type is not one of the known payroll-changing types, it is not auto-applied.
That means the system is biased toward not acting unless the notice is clearly understood.
2. Previously applied notices stay applied
One of the subtle problems in integrations like this is duplicate processing. HMRC retrieval can surface notices that Workmax has already seen before. If the system blindly inserted fresh copies every time, the same notice could be re-applied more than once.
To prevent that, the retrieval pipeline persists notices through persistRetrievedCodingNotices(), which checks for existing records and preserves the applied state. In other words, retrieval is not treated as “brand new by default.”
That gives the feature an important idempotency property: repeated retrieval does not mean repeated payroll changes.
3. Missing employee email blocks the side effects
We decided not to silently apply payroll-affecting notices if the employee cannot be notified. Before the automation sends side effects, it validates that actionable notices map to employees with email addresses.
This is intentionally strict. If the notice changes payroll, Workmax should also be able to tell the employee and relevant admins what happened.
4. Informational notices still remain visible
Automation does not hide the underlying notices. The UI still surfaces the notice stream and the applied/received state, so teams retain visibility into what was processed automatically and what still needs review.
What Actually Changes on the Employee Record
The most important part of this work is the transformation layer in coding-notices-utils.ts.
That is where notice types are converted into concrete payroll changes:
P6,P6B, andP9update tax code-related fields.SL1andPGL1add loan deductions where appropriate.SL2andPGL2remove loan deductions when HMRC instructs payroll to stop taking them.
This logic is paired with tests that cover person matching, duplicate prevention, tax code updates, and loan start/stop scenarios. For a compliance feature, that test coverage is not optional; it is the feature.
Notifications: Not Just “Applied,” But Explained
Applying the notice is only half the job. The other half is making the change understandable.
The new automation flow sends:
- an employee email
- admin email notifications
- in-app notifications
The email content is built dynamically in coding-notices-automation.ts, and delivered through a dedicated SES template in apps/workmax-api/email-templates/coding-notice.html.
Rather than sending a generic “something changed” message, the code builds notice-specific explanations. For example:
- a
P6email explains that HMRC has instructed the employer to use a new tax code - an
SL1email explains that student loan deductions will start - a
PGL2email explains that postgraduate loan deductions will stop
That extra detail matters because payroll users do not experience these changes as abstract event types. They experience them as differences in pay.
The Admin Experience: A Toggle, Not a Forced Migration
We made the feature opt-in through HMRC settings rather than switching companies over automatically.
CodingNoticeSettings now includes an autoApplyCodingNotices flag alongside the per-stream retrieval indexes. In the Angular UI, the HMRC settings dialog exposes this as a clear slide toggle:
Auto apply actionable coding notices
The wording is deliberate. It tells admins that:
- the feature is automatic
- it applies only actionable notice types
- informational notice types still stay visible for manual review
The same settings screen also keeps the retrieval indexes visible, which is useful for support and operational debugging when a company needs to understand where Workmax will continue from on the next HMRC fetch.
Why This Is Better Than “Just Add a Cron Job”
It is tempting to think of this feature as a small automation layer on top of HMRC retrieval, but the real work is in the boundaries:
- when the system should act
- when it should not act
- how it avoids duplicate application
- how it communicates the change
- how admins can control it
That is why this rollout touches Lambda functions, DynamoDB access, GraphQL models, Angular settings screens, notification models, email templating, and tests. In a payroll system, “automation” is never just background execution. It is product behavior, auditability, and trust all at once.
The Payoff
With this change, Workmax moves coding notice handling from a fully manual queue to a controlled automation flow:
- retrieval remains visible
- actionable notices can be applied immediately
- employees are informed
- admins are informed
- previously applied notices are not replayed
- non-actionable notices still wait for review
That combination is the real win. We reduced administrative friction, but we did it without flattening the nuance out of HMRC notice handling.
For payroll software, that is usually the difference between automation that feels risky and automation that teams can actually trust.
Interested in more engineering deep dives? Explore the other posts in blogs/ for more Workmax build notes.


