Sajjad Mahessar Good Merch Group Free audit
~/sajjad $ cat projects/serviced-apartments ← cd ../projects Client work

Serviced-apartment access & pricing platform

Live · real guestsPaying client

The first system built and sold through my own company, now taking real guests. Five serviced apartments in one building, run end to end: a staff dashboard, a separate guest check-in site, door codes that exist only during the stay, cleaner alerts on checkout, late-checkout requests, pricing from local event demand, and profit reported per operating company.

Type
Client platform
Category
Client work
Built with
Node.js / Express · SQLite · AES-256-GCM · HMAC tokens · Railway · Resend
Project
3 of 37
BOOKING CONFIRMED CODE ISSUEDDAYS AHEAD AES-256-GCMSTORED ENCRYPTED WRITTEN TOTHE LOCK WHAT THE GUEST SEES BEFORE · NOTHINGAFTER · NOTHING DURING · THE CODE LINK IS HMAC-SIGNED · CANNOT BE EDITED TO POINT ELSEWHERE A FORWARDED LINK IS WORTHLESS OUTSIDE THE WINDOW
Encrypted at rest, revealed only inside the stay window, gone at checkout

How the secrets are handled

  • Every door code is encrypted with AES-256-GCM before it reaches the database. A stolen database dump contains ciphertext and nothing else.
  • Guest check-in links are HMAC-signed tokens carrying the booking and an expiry — not guessable, and not editable to point at another booking.
  • Staff passwords are scrypt-hashed with a per-user salt; webhooks are rejected unless the signature matches the raw body.
  • Neither key is ever sent to a browser. The guest page asks the server what it is allowed to see and gets back a code or nothing.
Rotating the signing secret invalidates every outstanding link at once — which is also the emergency lever if one ever leaks.

Two decisions that come from operations, not code

Codes are issued at booking confirmation, not at check-in. The PIN is written to the lock days ahead and lives in the lock’s own memory, so a broadband fault on arrival day cannot lock a guest out of the building at eleven at night.

Rotation writes the new code before retiring the old one. If the lock is unreachable the old code keeps working and the failure is logged — the opposite order would leave a cleaner standing outside a flat they can’t enter.

Staff codes rotate on a seven-day cycle because they have no natural end. A code that leaks with a departed cleaner, a contractor or a photographed keypad stops working within the week without anyone remembering to change it.

Built before the hardware existed

The lock provider sits behind an interface with a mock implementation, so every path — issue, reveal, revoke at checkout, rotate — runs for real against a simulated lock. One environment variable swaps the mock for the live provider when locks are installed. No application code changes.

Messaging works the same way: dry run is the default. With no API keys set, every email and SMS is composed, stored and shown on the dashboard but nothing is sent — so the whole checkout flow is testable for free, without texting anyone.

Pricing from local demand

  • Three event sources refreshed daily and merged into one table — a venue what’s-on page, a ticketing API and a fixtures feed — because the trade shows that actually drive midweek demand never appear in ticketing APIs.
  • The scraper runs once a day, identifies itself with a real contact address, uses conditional requests so an unchanged page costs a few bytes, takes facts only, and stops on a 403 rather than retrying — a bad fetch leaves yesterday’s events in place rather than emptying the calendar.
  • Long-running exhibitions are capped in the weighting: a three-month immersive experience is background footfall, not a spike.
  • The demand weightings are documented as informed guesses to be replaced by the client’s own occupancy data after a season, rather than presented as fact.

What the guest gets

  • A personal stay page: wifi for their apartment with tap-to-copy, directions, check-in steps, photos, the house manual, emergency contacts, and local exhibition and concert dates that fall inside their stay.
  • Door and entrance codes that appear at 15:00 on arrival and vanish at 10:00 on departure — on UK time, correct across the clock change, which is exactly the night a naive timestamp gets wrong.
  • An arrival message sent automatically from the property’s own domain under the building’s name, verified for inbox delivery, with replies routed back for handling.
  • Late checkout from their own page. On approval the checkout time and the door code both move automatically, and the fee is collected through whichever platform they booked on.
The page can be shared with the rest of the party, so one booker doesn’t become the group’s switchboard — without the code ever being visible outside the stay window.

Turnaround without phone calls

Guest checkout starts the chain. The cleaner is alerted the moment a flat is free, the guest’s code closes at the same time, and the unit goes back on sale. Cleaners work from a personal link with nothing to remember, and upload photos for review.

Cleaning photos delete themselves after 30 days unless flagged as damage evidence. Enough to settle a dispute, not a growing archive of other people’s bedrooms.

Revenue side

  • Launch rates set from a local competitor pricing analysis, with separate bands for studios and two-bedroom apartments, loaded twelve months ahead.
  • A built-in prompt to move to demand-based pricing once enough stays have completed — rather than pretending to have data on day one.
  • Occupancy, revenue and profit reported split by operating company, because the apartments are let through more than one business and the books have to separate cleanly.

Run as a client engagement

Scoped from a setup questionnaire, delivered under a signed service agreement, and closed out with a delivery record listing all 24 items built and every item still owed by the client — platform accounts, photographs, lock hardware — with what each one blocked.

The delivery date was tied explicitly to those inputs. A build that’s finished on my side but can’t open without the client’s photos is documented as exactly that, not reported as late.

What’s written down as not done

  • Two-factor for staff, and secrets moved out of environment files into a managed store.
  • Automated encrypted backups with one tested restore — untested is not a backup.
  • A data processing agreement with the client, ICO registration, and a retention job deleting guest personal data on a schedule.
  • The lock adapter swaps in with one setting once hardware is fitted; a day is budgeted for field issues rather than assuming the first install goes clean.
Handed over as a checklist the client can hold me to, rather than a system described as finished.
iOS app

Staff management iOS app

Workforce management on the App Store

SwiftUISupabaseAPNsEdge Functions
Reporting

Weekly food & GP reporting

Weekly P&L every figure can be traced

PythonReportLabEPOS exports
No charge

I’ll find the money before you pay me anything.

Send me your software subscriptions and I’ll come back with what’s replaceable and what it’s worth — free, and yours to keep either way. How it works →

Contact

Want the same done to your cost base?

Open to operations, automation and technical operations roles — and to talking with anyone running a multi-site business who suspects they're paying for software they could own instead.