Features · Customer flow

The booking flow your customers actually finish.

A 5-step funnel designed around one principle: every screen has a forward path. No dead ends, no greyed-out buttons without a reason, no “sorry, sold out” walls.

The 5 steps

Every booking moves through five steps. The catalog step is skipped automatically when the widget is opened with a deep link to a specific product, so most traffic from your marketing site lands directly on the calendar for the boat or experience they clicked.

Catalog

Grid of all bookable products. Skipped entirely when the widget opens with ?product=<id> — which is how every “Book Now” button on your marketing site should link, so customers go straight to the calendar.

Calendar

Date picker with availability shown per day. The default style is a compact month view with colour-coded availability dots, so customers can see “open / nearly full / sold out” at a glance. Several visual variants are available per tenant.

Time

Horizontal list of start times for the chosen date. Times are filtered by remaining capacity and by per-tier “latest start time” rules — e.g. if a 6-hour charter can’t start after 11am, 2pm doesn’t appear even if the slot is technically open.

Options

Pricing tier (flat-rate vs per-person), guest count, optional add-ons (skipper, esky, photographer), waiver acceptance, and promo / gift-card code entry. One screen, one “Continue” button.

Checkout

Contact details, terms acceptance, and a single “Pay” button. Submitting creates a booking draft and hands off to Stripe Checkout for payment.

Zero Dead Ends — the load-bearing UX rule

Every screen, in every state, must offer at least one valid path forward. A “sorry, gone” screen anywhere in the funnel is a bug.

The principle comes from Luke Wroblewski (Mobile First) and is the design anchor of the entire booking flow. We benchmarked seven platforms — OpenTable, Calendly, Stripe Checkout, Airbnb, Resy/Tock, Hopper, Klook — and synthesised what they do well into 14 patterns that drive every screen.

The alternatives sheet

The flagship of the pattern. When a customer clicks a sold-out date or a sold-out time, they don’t see “unavailable” — a sheet opens with three sections, in priority order:

  • Same day, sister product. If you have other boats or experiences in the same category with availability on the chosen date, they’re shown first. Picking one swaps the selected product and jumps the customer straight back to the options step.
  • Same day, different time. Shown when only the time was sold out and the same product still has open slots that day.
  • Other dates, same product. The next six available dates for the original product, sorted by closeness to the date the customer wanted.
  • Phone fallback. If the entire window is full, the sheet falls through to “Fully booked — give us a call, sometimes we can fit you in if there’s a cancellation.”

Why this matters

A customer who sees “sold out” usually leaves and books a competitor. A customer who sees “sold out, but how about this on the same day, or next Saturday?” usually picks one of the alternatives. The alternatives sheet is the difference between losing the booking and saving it.

Mobile-first, with full-page takeover

On mobile, the booking widget runs as a full-page takeover — no cramped iframe inside a tiny viewport, no double-scroll. The mobile flow is its own component tree, designed mobile-first, and bakes the Zero Dead Ends rules into every step.

Mobile v2 is enabled per tenant via a feature flag, so an existing operator with customers used to the original flow can stay on it until they’re ready to switch.

Payments

Payments go through Stripe Checkout — the hosted page, not Stripe Elements. The hosted page handles PCI scope, 3D Secure, Apple Pay, Google Pay and Klarna for free, so the booking widget doesn’t carry payment-card code at all.

For customers who want to pay another way, you can enable bank transfer as a parallel option — the booking is created in “pending” state with the bank details emailed, and you mark it paid from the admin once the transfer arrives.

Gift cards can be sold and redeemed end-to-end: purchase, balance check, partial redemption against a booking, and full lifecycle management from the admin.

Promo codes are validated live during checkout via a dedicated endpoint, so customers see the discount applied before they hit pay.

Confirmations — email, SMS, and a manage page

After payment, the customer gets:

  • An email confirmation via Resend, rendered from a React Email template. The header, intro, sign-off, button label and footer note are all editable from your admin panel — you control the voice, the platform controls the structure.
  • An SMS confirmation via Twilio, when SMS is enabled for your tenant. Twilio credentials are per-tenant, so each operator uses their own Twilio account and sender ID.
  • A reminder 24–48 hours before the booking, sent by an internal cron job — email always, plus SMS if Twilio is enabled for that tenant. Same operator-editable copy model.
  • A manage-booking page at /booking/manage. Customers look up their booking with reference code + email and can either reschedule it, or cancel it (auto-refunded if they’re still inside the cancellation window). No need for them to phone you.

Deep linking from your marketing site

Every “Book Now” button on your marketing site can link directly to a specific product:

<a href="/book/?product=fh_skipper_six">Book Now</a>

The widget reads the product query parameter, skips the catalog step, and lands the customer on the calendar for that product. If the product ID is invalid (e.g. a stale link), it falls through gracefully to the full catalog instead of erroring.