Devlights · White-label B2B SaaS
PeopleDesk. One platform for the whole company.
A white-label HR, payroll and finance product for a software agency to resell, designed end to end around a single idea: one employee record, seen through the lens each team actually needs.
What this is
PeopleDesk is a product Devlights set out to build as a white-label platform: one HR, payroll and finance system a company could brand as its own. The brief was broad on purpose. It had to cover the whole employee lifecycle, from a job posting through to a monthly payment, and it had to hold data sensitive enough that access itself became a design problem.
I designed it alone, from the interviews through to the handoff. To keep the scope honest I anchored everything to one decision made early: a single employee record that every module reads from, and a permission layer on top of it so HR, finance and an external consultant each see a different version of the same person.
Because it is a concept built to be resold rather than a product with live users, this case is about the reasoning and the system, not adoption numbers. Where a metric would normally sit, I say what I would measure instead of inventing a figure.
The problem
Five tools, and a person paying salaries by hand
Companies run their people across disconnected tools: an HRIS for contracts, a payroll processor, a spreadsheet for hours, an applicant tracker for hiring, and an accounting tool none of them talk to. The interviews kept returning to the same image at the end of that chain, someone opening a spreadsheet and paying each employee one at a time, every month, hoping nothing had drifted out of sync since last cycle.
Hours live in one system, salaries in another, hiring in a third, and the numbers only meet when a person reconciles them by hand. Every seam between tools is a place for a payroll error to hide.
Payroll was manual and unverifiable
An accountant copied figures out of a spreadsheet and paid people individually. Nothing checked the numbers before the money moved, and an error could sit undiscovered for months.
Hiring waited on engineering
Publishing a role meant emailing the dev team to edit the website, then tracking candidates in a shared sheet that lost people between cells.
Finance always saw last month
The finance lead got a monthly PDF from HR. No way to drill in, no live cost, no split by project. They were always reporting on a period that had already closed.
Sensitive data had no real walls
Salaries and performance notes sat in shared files anyone could open. There was no separation between what HR needed and what finance needed to never see.
The core insight. The three teams did not need three tools. They needed the same data seen through three lenses. HR runs the people, finance runs the money, and neither should see the other's most sensitive fields. That single realisation, one record with per-field permissions, shaped every decision that followed.
Who sees what is the hard part
In a product holding salaries, performance reviews and personal data, access control is not a settings screen bolted on at the end. It is the constraint that shapes the interface, because the same record has to render differently depending on who opens it. I designed around least privilege: each role sees exactly what its job needs, and nothing more.
The owner sees everything and is the only role that can grant access. HR runs the people but never sees a salary figure. Finance runs the money but never reads a performance review. A consultant sees only non-sensitive operational data. The permission model is per field, not per page, so the same employee record can show pay to finance and reviews to HR without either crossing over.
Three principles under every screen
One record, many lenses
Every module reads from the same employee. A hire entered once is never retyped, finance never sees a stale copy, and permissions decide which fields each role can read. The single source of truth is what makes the whole thing feel like one product instead of five.
Friction only where it is irreversible
Most of the product gets out of the way. The exceptions are the moments money moves or a payment cannot be undone, where a deliberate gate slows the user down on purpose. Friction is a tool, spent precisely, not spread evenly.
Automate the labour, keep the judgement
Payroll and recurring costs run themselves, but a person still reviews and confirms every run. The goal was to remove repetitive work, not human accountability over an irreversible action.
The redesign
Finance reads the same data, live
The clearest proof that one shared record works is the finance dashboard. It never asks anyone to enter data. It visualises the exact records HR and payroll already maintain, updating the moment a run completes, so the finance lead reads this month instead of reporting on last month. This is the payoff of every decision in act one, shown on one screen.
The decisions that shaped it
Three places where the interesting part is the reasoning, not the pixels. Each one pairs a design choice with the constraint that forced it.
The friction gate
Running payroll is the one irreversible action in the product. Confirming it requires typing the total amount by hand, borrowed from how banking apps release a transfer.
Why it works this way
Accidental runs were the sharpest fear in the interviews. Placing the friction exactly at the irreversible step, and nowhere else, buys peace of mind without slowing the rest of the flow. Everything up to this point stays fast.
Per-field permissions, made visible
The same employee record shows salary to finance and performance notes to HR, never both to either. Where a section is hidden, a lock note says so explicitly rather than leaving a silent gap.
Why it works this way
Hiding whole pages would have been simpler, but it forces duplicate records and breaks the single source of truth. Field-level control is harder to build and it is the only version that keeps one record serving three roles. The lock note turns an absence into a statement of intent.
Publishing without engineering
HR writes a role once and publishes it. It syncs to the public careers page instantly, no developer in the loop, and the structured fields entered here, type, project, manager, are reused when the hire becomes an employee.
Why it works this way
The old path made recruiters wait days on the dev team to edit a website. Removing that dependency was the point, and reusing the same fields downstream is what lets a hire flow into payroll with nothing retyped.
Running payroll, end to end
The core flow follows three phases: review what will be paid, resolve anything blocking, then confirm with the full summary in view. Critical issues are hard blockers, not dismissible warnings, because the cost of paying out bad data is worse than the friction of a hard stop.
From job post to first payroll
The hiring flow closes the loop back to payroll. A role published without engineering collects candidates in a pipeline, and the hire converts into an employee record that flows into the next pay cycle with nothing re-entered.
The rest of the lifecycle
Beyond the two core flows, the product covers the routine that eats an HR team's week: time off, approvals, and the recurring costs finance sets once and only touches when something changes.
Three roles kept describing the same data, and three different fears about who else could see it.
Recurring theme across the research interviews
Step 1Interviews
I interviewed people across different roles inside a company, HR, recruitment and finance, to understand how each one works today and what they need from the others.
Step 2Mapping
Their answers turned into the three-lens model: the same employee record, read differently per role, with a permission layer as the thing that made it safe.
Step 3Design
The two highest-stakes flows, payroll and hiring, were designed in full with their edge and error states, then the surrounding modules on the same system.
What I decided not to build
Two choices where the point is what I gave up, and why, including the argument against my own decision.
Predefined roles, not a permission grid
An early version let admins toggle access per person across a grid of features. Maximum flexibility, and a liability: it was easy to accidentally grant finance access to performance data. I replaced it with four predefined roles that cover the real cases, with custom overrides available but off the default path.
The honest caveat: predefined roles will not fit every company that resells this. A larger org may genuinely need finer control. The bet is that secure-by-default matters more than flexibility for most, and that the override exists for the rest. If resellers keep hitting the edges of four roles, the grid comes back, built more carefully.
Human confirmation, not full automation
Payroll could run fully automatically once alerts clear. I kept a person in the loop, reviewing and typing the total to confirm. More automation was possible and I chose against it.
Removing the final human confirmation removes accountability for an irreversible action worth a month of salaries. The labour worth automating is the reconciliation, not the decision to release the money. That line is where I stopped.
The outcome
What shipped, and what I will not claim
What exists is a complete, self-consistent product: three role-based domains on one shared employee record, two core flows designed through their edge and error states, a finance module that reads live, and a permission model that lets one record safely serve teams with opposing access needs. It is ready to be branded and resold, which was the brief.
What I cannot claim. This is a concept built for a white-label sale, not a product with live users. There is no adoption number, no reconciliation-time saving, no error rate, because there is no production data to measure. Inventing one would be worse than its absence.
What I would measure. If a reseller shipped this, the first questions would be time to run a payroll cycle end to end, the rate of runs blocked by a caught error, and how often the four roles need a custom override. Those would tell me whether the core bets held.