Skip to content

RFC: support request-first direct bookings with staff approval #332

Description

@agustinjch

Use case

Some properties need to review a guest's request before accepting payment and creating a reservation. The guest selects dates and an offer, submits contact/application information, and receives a request acknowledgement. Staff then reviews, approves, or rejects it. Approval revalidates availability and price; only successful payment creates the reservation.

This differs from instant booking and from demand capture after a sellout. It should be an opt-in property mode, while existing booking-engine behavior remains unchanged by default.

Current HAIP primitives

At baseline 922bea056e89f75cc476c327c040400767c0446b:

  • the booking engine creates a reservation through the instant booking path;
  • waitlist_entries is non-inventory-deducting and already stores dates, occupancy, room/rate references, guest contact details, and active -> offered -> converted/cancelled/expired state;
  • WaitlistService.convert() checks availability and returns needsBooking: true, but does not perform a public request workflow or a payment-safe conversion;
  • waitlist routes are staff-authenticated and there is no request-specific public management token.

Relevant code:

  • packages/database/src/schema/demand-capture.ts
  • apps/api/src/modules/waitlist/waitlist.service.ts
  • apps/api/src/modules/waitlist/waitlist.controller.ts
  • apps/api/src/modules/booking-engine/booking-engine.service.ts

Required invariants

  1. Request mode is property-configurable; existing properties continue using instant booking.
  2. Submitting a request never creates a reservation or reduces availability.
  3. A public booking key may submit a request but cannot enumerate requests.
  4. Public follow-up access is scoped to one high-entropy request token stored only as a hash.
  5. Staff review, rejection, withdrawal, and expiry are explicit and auditable states.
  6. Approval recalculates the authoritative quote and rechecks availability.
  7. A failed or incomplete payment creates no reservation.
  8. Retrying approval cannot create a second payment or reservation.
  9. All reads and transitions remain property-scoped.
  10. Existing instant-booking API and widget behavior remain backward compatible.

Domain-model decision requested

Should this workflow:

  1. generalize waitlist_entries into a broader non-deducting demand/request aggregate; or
  2. introduce a separate booking_requests aggregate and leave waitlist semantics focused on unavailable demand?

A booking request needs review/payment states, an immutable quote snapshot, structured application data, consent metadata, public token scope, and conversion identifiers. Those requirements may make a separate aggregate safer, but we do not want to duplicate an existing HAIP domain concept without maintainer direction.

Proposed contribution boundaries after approval

PR A — domain and persistence

  • Add the approved aggregate/state machine and property bookingMode: instant | request configuration.
  • Add tenant-scope, transition, and non-deducting behavior tests.
  • No Stripe or widget behavior in this PR.

PR B — submission and review

  • Add public submit/read/withdraw routes scoped by booking key plus request token.
  • Add staff list/review/reject routes and the minimal dashboard queue.
  • Switch the public widget's completion behavior only when request mode is enabled.
  • No card storage or approval-time payment in this PR.

Later payment PRs

  • Add SetupIntent/off-session consent, idempotent approval-time capture, and SCA recovery behind the accepted request model.
  • Never deploy an incomplete payment sequence as a production request flow.

Questions for maintainers

  1. Is waitlist the intended aggregate for this use case, or should request-first booking be separate?
  2. Which HAIP knowledge-base sections define request/option bookings and conversion?
  3. Should a request be allowed when availability is currently zero, or is that exclusively waitlist behavior?
  4. Which existing staff permissions should govern review and approval?
  5. Where should immutable quote/application snapshots live under HAIP's retention model?
  6. Is bookingMode the preferred property-level switch, or should request behavior attach to an offer/rate plan?

We can provide one focused, test-backed PR at a time after the aggregate boundary is confirmed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions