"Why is my pay different this month" is one of the highest-volume, highest-sensitivity questions an HR team fields, and it's a good candidate for an agent precisely because the answer is almost always deterministic — it's sitting in the payroll run's earning and deduction lines. The risk isn't that the agent can't find the answer; it's that a wrong or approximate answer about someone's pay is a much bigger problem than a wrong answer about an order status.
Answer from the payroll run, not from memory
The agent should never generate an explanation of a pay difference from general payroll knowledge — every claim needs to trace back to an actual field in the employee's payroll batch: gross pay, each earning type, each deduction, tax lines, and the prior period's equivalent for comparison. Structure the tool so it returns the current and prior period side by side with a computed delta per line, and have the agent narrate that structured diff rather than reconstruct one from a paragraph description.
def pay_diff(employee_id: str, current_period: str, prior_period: str) -> dict:
current = fetch_payroll_lines(employee_id, current_period)
prior = fetch_payroll_lines(employee_id, prior_period)
lines = {}
for code in set(current) | set(prior):
c, p = current.get(code, 0), prior.get(code, 0)
if c != p:
lines[code] = {"current": c, "prior": p, "delta": round(c - p, 2)}
return {"employee_id": employee_id, "changed_lines": lines,
"net_current": sum(current.values()), "net_prior": sum(prior.values())}
Scope the agent to the asking employee, only
This is the one area where an authorization mistake is not abstract — it's a privacy incident. The tool layer, not the prompt, must enforce that an employee-facing agent can only query the payroll records of the authenticated user making the request. Don't rely on the model to "remember" whose data it's allowed to see from context; pass the authenticated employee ID as a fixed parameter the model can't override, and reject any query where an argument the model supplies conflicts with it.
A manager asking about a direct report's pay is a legitimate use case, but it's a different authorization scope than self-service. Build it as a distinct tool with its own access check (is this person actually this employee's manager, checked against the org structure, not asserted by the caller) rather than widening the self-service tool's scope to cover it.
Explaining common variance causes correctly
Most pay differences trace to a handful of causes: a mid-period rate change, overtime or a bonus in one period but not the other, a benefits deduction that started or stopped, a tax bracket threshold crossed year-to-date, or a pay period with a different number of working days. Give the agent a lookup against the actual deduction and earning codes active for that employee rather than a generic list of "reasons pay might change" — a generic explanation that happens to be plausible but wrong is worse than "I can see gross pay changed by $340, driven by overtime code OT1, but I can't explain further — here's a link to raise this with payroll."
Know when to hand off
Some questions aren't data questions — a garnishment order, a leave-without-pay dispute, an error the employee believes was made. The agent's job in those cases is to summarize what the data shows and route to a human, not attempt a judgment call about whether a deduction was applied correctly. Define the handoff triggers explicitly (garnishments, disputes, anything the employee explicitly disagrees with) rather than leaving "should I escalate" to the model's discretion each time.
Regional payroll context matters
For a Kenya-based payroll (PAYE, NSSF, SHIF, housing levy) or similar regional setups elsewhere in East and Southern Africa, statutory deduction changes are a common source of "why did my pay change" questions that have nothing to do with the employer's payroll configuration — a new NSSF tier or a PAYE band adjustment shows up as a deduction change the agent needs to correctly attribute to a statutory change, not an internal payroll error, or it will generate support tickets that shouldn't exist.
| Cause of variance | Where it shows in the data |
|---|---|
| Overtime or bonus | New earning code line, not present prior period |
| Benefits change | Deduction code start/stop date |
| Statutory rate change | Same deduction code, different computed amount |
| Different working days | Gross salary line itself changes on a daily-rate basis |
Wrapping up
Ground every answer in the actual payroll run data with a structured period-over-period diff, enforce self-service scope at the tool layer rather than trusting the prompt, and hand off anything that's a dispute or judgment call rather than a data lookup. Payroll is one domain where "I don't know, let me connect you with someone" is a completely acceptable and often correct agent response.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.