A timesheet validator on AWS for a few dollars a month
Payroll week has a particular texture in a small business. Somebody prints the timesheets. Three are missing. Two have a Thursday with no hours on it. One says fourteen hours on a day the roster says was a half shift. Now it is the 28th, the people who could explain any of that have gone home, and payroll is Friday. The problem is not that the sheets are wrong. It is that nobody looked at them for three weeks. This post walks through a small system that looks at every sheet the minute it arrives.

Key takeaways
- Sheets are checked on submission, not at payroll. The gap between error and question is minutes.
- Three sources to check against: the roster, the job log, and a rules tab you already keep.
- Every sheet ends in one of three states: clean, asked about, or waiting on a manager.
- The system never edits an hour. It asks the person, or it flags it for someone with authority.
- Designed on AWS for about $3 a month at typical small-business volume.
The whole system on one page
Before any code, here is the shape of what we are designing.
- App integration
- Machine learning
- Management
- Analytics
- People
What you set up once (the outside)
- A way to submit. Whatever people already use. A web form for most, a photo of a paper sheet for the ones who will never use a form, and a spreadsheet upload for a supervisor who collects a crew’s hours. All three are covered in Part 2, and all three become the same thing: one row per person per day.
- A roster. Who was expected, when, and where. Most small businesses have this somewhere — a shared calendar, a scheduling app, a whiteboard photographed on Monday. It does not need to be authoritative. It needs to be roughly right, because its only job is to notice that Thursday is blank on a sheet for somebody who was rostered on Thursday.
- A rules tab. One sheet holding the things a payroll clerk carries in their head: the unpaid break after six hours, the point where overtime starts, the maximum day nobody should be working past without a conversation, the rounding rule. Writing them down once is most of the value of this project, independent of any code.
What runs on every sheet (the inside)
- The reader. Turns whatever arrived into rows. A form is already rows. A spreadsheet is nearly rows. A photo of a paper sheet is the hard case, and it is the one place a model earns its keep: reading a handwritten grid into start times, finish times and a job reference, and marking anything it could not read confidently as unreadable rather than as a number.
- The comparer. Five checks against the three sources, all arithmetic. Does every rostered day have hours? Does every day with hours have a job, or a reason it does not? Do the breaks satisfy the rule? Is any day longer than the maximum? Does the week’s total cross the overtime threshold, and if so has the sheet marked it as overtime?
- The asker. Turns a failed check into one message to the person who submitted it, with the day named and the discrepancy stated. Almost all of them are closable in one tap. The few that are not — an override, an unusual overtime week, a day past the maximum — go to a manager, because those need authority rather than memory.
One sheet, end to end
- App integration
- Machine learning
- Management
- Analytics
In plain words
Your site supervisor submits her week on Friday at four. The reader turns it into five rows. The comparer opens the roster: she was on Monday to Friday, and there are five rows, so nothing is missing. Four of the five days have a job reference that matches a job that ran that day. Tuesday says nine and a half hours with a thirty-minute break, and the rule says a break of thirty after six hours, so that is fine. The week totals 44 hours against a 40-hour threshold, and the sheet has marked four hours as overtime. Everything lines up. The sheet is marked clean, and nobody hears anything.
Her colleague submits at the same time with Thursday blank. The roster says he was on. Twenty seconds later he gets one message: “Thursday 9th is blank and the roster had you on the Aldershot job — hours, or were you off?” Two buttons: “I worked, here are the hours” and “I was off”. He taps the first, types 7 to 3.30, and the sheet is clean before he has left the car park. On the 28th, payroll opens twenty-two sheets and every one of them is right, which is a completely different week from the one that starts with printing them out and finding three gaps.
Design rules that shaped every decision
- Check on submission, never at payroll. The value is entirely in the gap between the error and the question, and that gap should be minutes.
- The system never edits an hour. It asks the person who wrote it, or it flags for someone with the authority to change it.
- Unreadable is a legitimate answer. A handwritten 7 that might be a 1 is marked unreadable and asked about; it is never resolved by guessing.
- The roster is a hint, not a law. People swap shifts. A mismatch is a question, not a correction.
- The rules live in a sheet. Changing the overtime threshold is an edit, not a deploy.
- A clean sheet generates no message to anybody, ever. Most sheets are clean.
Why this shape
Nearly every timesheet problem is a timing problem wearing a data costume. The information needed to fix a blank Thursday exists in one person’s head on Friday and has largely evaporated by the 28th. A validator that runs at payroll time is therefore solving the problem at the worst possible moment: maximum cost to fix, minimum memory available, and a deadline.
So the design puts almost nothing clever in the checks and everything into when they run. Five arithmetic comparisons against records you already keep, executed within a minute of submission, closing the loop with the one person who can answer. The result is not a smarter payroll process. It is a payroll process with nothing in it to discover.
The next four posts walk through each piece: how a timesheet arrives, how it gets compared, how a question reaches the person who filled it in, and how a week gets closed. One diagram per post, a cost breakdown, and an engineering reference at the end.
All posts