SiteBooks: construction management for a client who cannot visit the site
2026-09-22 / 4 min / construction / multi-tenant / m-pesa / laravel / offline-first
Four years of production construction software for contractors and the diaspora clients funding the build. Time-stamped site photos, budgets tracked against the BOQ, milestone payments, and M-PESA payroll for casuals, all of it usable on 3G.
The hard problem is not scheduling
Construction software usually sells you a Gantt chart. That is not what goes wrong on a Kenyan build funded from abroad.
What goes wrong is trust across eight time zones. Someone in London is sending money every month for a house they have never stood in. The contractor is honest, busy, and on site. The gap between those two people is where the relationship fails, and no amount of scheduling closes it.
SiteBooks is shaped around that gap. It is live at mysitebooks.com, operated by Mjengo Technologies LTD, and it has been in continuous production since August 2022.
Four years in, it has carried more than 100 construction projects and over KES 50M of client money: milestone payments released against work that was evidenced, and M-PESA payroll paid out to casuals on site. Those two numbers are the only ones that matter here, because they are the two things a client abroad is actually trusting the software with.
Two users who will never meet
The whole design follows from having two users with opposite constraints.
The foreman is on site, on a phone, on 3G, with dusty hands and about forty seconds of patience. If logging today’s progress takes longer than that, it does not happen, and a tool nobody updates is worse than no tool because it lies with a timestamp on it.
The client is abroad, on good bandwidth, reviewing on their phone before bed, and looking for one thing: evidence that the money did what it was supposed to do.
So the capture side is brutally short and the review side is generous. The same event, a day’s progress, is a sixty-second form on one end and a photo set with a budget line attached on the other.
Building for the worst connection on the site
This is the decision that shaped the most code, and it is the reason the capture side works at all.
Every mobile-first framework treats poor connectivity as a degraded state to apologise for. On a Kenyan construction site it is the normal state, and the site is often the one place with the worst signal for a kilometre.
A build with no roof yet has no wifi. That is not an edge case to handle later; it is Tuesday.
Practically that meant: photos upload in the background and survive the app being closed, a submitted log is queued locally and confirmed when it lands rather than blocking on the round trip, and nothing important is behind a request that must succeed while the user waits. The foreman finds out their log was accepted; they do not sit and watch a spinner to learn it.
It also meant being disciplined about payloads on the review side, because “client abroad” frequently means a phone on a train.
The BOQ is the budget, not a spreadsheet next to it
A bill of quantities already exists on every real build. It is the contract. Tracking spend in a separate system and reconciling it to the BOQ by hand is the same mistake as tracking invoices separately from payments: it creates a second number, and second numbers are where arguments start.
Spend is recorded against BOQ lines directly, so “are we over budget” is a query rather than an exercise. Milestone payments hang off the same structure: a milestone is a set of BOQ lines reaching a state, which is what the client is actually paying against.
M-PESA payroll for casuals
Casual labour is paid in cash, weekly, to people who may not be the same people as last week. That is a reconciliation problem and a safety problem at once: cash on site is a target.
Paying casuals over M-PESA from the same system that holds the day’s attendance turns payroll into a record instead of an envelope. The client abroad sees labour cost as a line that reconciles, and nobody drives to site with a bag of cash.
How it is built
The platform is a monorepo of surfaces that ship independently. A Laravel core on PHP 8.5 with Blade, Livewire and Horizon is the product surface for staff and clients, and also exposes a token-authenticated JSON API. A React dashboard on Vite sits over that API at parity with the Livewire surface. The public marketing site is its own Laravel app.
Four years in, the thing I would defend hardest is that the API came before the second front end. The React dashboard was built against a contract that already had a working consumer, which is why two surfaces have not drifted into two products.
What four years teaches you
Every feature that assumed good connectivity had to be rewritten. Every feature that assumed the foreman would care about data hygiene had to be rewritten. The features that survived unchanged are the ones that asked for the least and showed the most.
If you are building for users whose connection, device and patience are all constrained, send me a brief. It changes more of the architecture than people expect.
Read next
- Noria Pay: one payments service instead of twenty-one integrations
Every product that took money had rebuilt the same layer: persist the transaction, host the callback, normalise the payload, reconcile the settlement. Roughly 4,800 lines of it in one codebase and 12,000 in another. This is that layer, written once.
- Noria Send: email and SMS behind one API
A Resend-shaped service over Amazon SES and OnFon Media. One outbox, one event stream, one suppression list, and a careful answer to the hardest question in delivery: what to do when a provider never replies at all.
