
The single write path re-proved every guarantee except the one the owner had just set.
Reservo is a multi-tenant appointment and booking platform for service businesses — salons, clinics, studios — sold as the system a business deploys under its own brand, rather than as a marketplace a customer browses to find a business. A service business configures its services, staff, and working hours; its customers book through a public per-business page, optionally pay a deposit, and receive automatic email reminders. The business gets a multi-staff calendar, configurable reschedule and cancel policies, customer records with no-show history, and occupancy and revenue reporting.
The product's central architectural claim is that availability is advisory and bookings are authoritative. A public slot grid shows what appears to be free, but nothing it reports can be trusted as a write guarantee: every real precondition — that the business is enabled, the service active, the staff assigned, no overlap — is re-proved inside a single database function that holds a transaction-level lock before it checks. No route may insert a booking directly.
What the function did not prove, until after the product was declared complete, was that the business was open. A schedule block correctly hid times from the grid — showing zero available slots on a closed day — and did nothing to the write path. Measured live in three steps: 56 available slots, a whole-business all-day block created through the application, 0 slots returned, and then a direct booking request on that date returning 201, confirmed. The salon was closed for a public holiday and the public page still took the appointment.
The fix went into exactly one place, which is the point. Because the architecture says every guarantee is re-proved inside the write, adding the block check to a route would have recreated the trusted outside-check the architecture forbids. The check went into the function. An authority that re-proves everything it knows about is only as complete as its enumerated list, and the clause most likely to be absent is the one for the rule most recently added.
Starting point
The booking engine was built around three correctness rules, each named in the source before any code beneath them was written.
Slot generation steps by service duration plus buffer, not by a constant interval. A 30-minute step offers a 10:30 start for a 45-minute service and rejects the booking at the write, after the customer has already filled in their details — the worst possible ordering. Availability times are stored in the business's own timezone; reading them as UTC shifts the entire week by the offset and is undetectable in any testing environment whose offset happens to be zero. The customer-visible end of an appointment is start plus duration; the buffer advances the engine's cursor and widens the occupied interval, but it is never shown to the customer.
The product deliberately does not function as a consumer marketplace. A business that deploys Reservo keeps its own customer relationships; the booking system is an operational tool, not a discovery surface. Marketplace features and point-of-sale inventory were outside the scope by business definition.
Reading the market
The booking space prices by the staff member. The reference incumbent — Booksy, whose multi-staff appointment agenda is the closest feature analogue — structures its fees as a flat base plus a recurring per-seat charge for each additional provider. The tool does not grow more capable as the business hires; the invoice does. Per-seat or per-provider metering is the category norm, expressed differently by different vendors but present throughout.
Reservo's own subscription plans are flat for small teams. The buyer receives a codebase with those defaults already seeded and the freedom to re-price after purchase.
The free end of the market has a gap. Fresha retired its free plan around August 2025. The remaining free incumbent worth naming plainly is Setmore Free — capped at four users and 200 bookings a month, a genuine competitive product for a two-person operation that stays under that volume.
Cal.com merits a separate note. It was the closest thing the scheduling space had to a self-hostable, MIT-licensed calendar tool. On 2026-04-14, Cal.com's core repository went closed-source. The fork cal.diy remains MIT-licensed but its own documentation disclaims production use. The category of open, self-hostable scheduling infrastructure has a vacancy it did not have a year ago.
The decisions that shaped it
One write path, and the grid is advisory. Every booking goes through a single database function: it acquires a transaction-level lock keyed on the staff member and start time, re-proves every precondition, and inserts. A partial unique index on the same key is the hard stop underneath. The concurrency guarantee was measured: the same slot submitted simultaneously by two callers produced exactly one confirmed booking and one rejected one. The advisory lock holds the concurrency case; the enumerated clause list was what failed the schedule-block case.
Three independent layers of reminder deduplication. Reminder sends are deduplicated at the delivery queue level, at a processed-events table, and at a unique constraint in the reminders table's own schema. The project rule, written explicitly in the source: an ON CONFLICT clause without the unique constraint it names is not a safeguard; it is a bug. The queue credentials were not configured in this deployment, so this layer is unexercised here.
Two webhook endpoints, two secrets, never shared. Platform subscription revenue and per-tenant deposit revenue both flow through the same payment provider. They use separate endpoints with separate verification keys. A shared secret would mean a signature valid for one plane is valid for the other.
A calendar outage cannot block a booking. External calendar sync runs as advisory overlay data. The sync watermark advances only after a successful batch commit; on failure, the error moves and the watermark deliberately does not. This prevents a failed feed from reporting itself as current — a week of silent failures reading as "synced." iCal read-only polling is what ships; Google and Microsoft OAuth are roadmap.
What fought back
The schedule block story is in the opening. Two related failures from the same post-completion audit pass complete the picture.
The write path for availability was broken the entire time. Saving business hours always failed. The interface returned "Could not save your hours"; the database's actual error — a column reference ambiguous between a loop variable and a function alias used in the same write statement — never surfaced in the UI. The function's validation loop was fine; only the write itself failed. Seeded availability worked, which is why the booking flow tested clean end-to-end: reads were never the broken half. The defect was found by a person reviewing the product after automated checks had all reported green.
The availability editor stated the opposite of what the engine does. A staff member with no saved rules of their own inherits the business default and is fully bookable across the business day. The editor rendered all seven days as "Not working." On the demo business, two staff members with zero saved rules — fully bookable Monday to Friday — showed greyed rows with every toggle off. An owner trying to correct this would toggle one day on, taking that person from a full business week to a single available day. The interface presented a reduction in availability as if it were an addition.
A security fix that its own re-test disproved, then proved. A crafted calendar feed URL could reach internal network addresses. The first fix applied a hardened wrapper with DNS pinning and private-target blocking. That fix was retested against the original attack pattern rather than accepted as sufficient: a redirect to a bare IP literal bypassed the pin, because the connection layer short-circuits on an already-resolved address and never invokes the custom resolver. A second fix stopped following redirects automatically and re-validated every hop as a fresh untrusted URL, bounded to three hops. The re-test carried a live control confirming the harness still reached legitimate targets when redirect-following was re-enabled.
The honest v1 limits. The staff-invite endpoint exists, is guarded, and is correct; it has zero callers, and the three interface controls that would invoke it ship disabled with a visible explanation — in a multi-staff booking product, this is the core buyer flow. No provider credentials were configured in this deployment, so deposits, email reminders, and the job queue are inert paths here; each degrades loudly rather than silently. A business's contact details are not modelled: phone and address controls were removed from the public booking page rather than left pointing nowhere. Intake answers are session-only — they reach the endpoint correctly on the normal same-tab path but are lost when the confirmation URL is opened in a fresh tab. SMS reminders are roadmap; the phone number is captured now. Courts and shared spaces are absent entirely — no backing table, the string appears zero times in the application source — a scope boundary drawn before the build, not an implementation that fell short. Group classes are the adjacent case: the capacity logic is in the write function and the schema, a distinct "class full" reason is returned, the feature flag defaults off, and the use case is sequenced for a later expansion. The two deferrals look similar; only one left traces.
What shipped
A complete multi-tenant build: a public booking plane, a multi-staff calendar, deposits through per-tenant connected accounts, three-layer-deduplicated email reminders, read-only calendar sync as advisory overlay, configurable reschedule and cancel policies with deposit forfeiture, schedule blocks and exception hours, customer records with no-show history, and occupancy and revenue reporting. Distinct roles across tenant and platform planes, full row-level isolation, and database-driven plans with per-plan feature gating.
The concurrent-slot guarantee was measured against a live remote database — same slot, two callers, one confirmation and one rejection. The permission matrix was executed across roles with no cross-tenant leak. Security review returned zero critical findings; three high-severity findings were adjudicated non-blocking and documented.
One dated fact frames the self-host argument. A self-hostable scheduling tool closed its source in April 2026, which is the risk an own-the-code buyer is pricing against. After purchase, the ceiling is the operator's own infrastructure.
