Restaurant Flavor
The restaurant flavor extends the generic Calendly-style flow with two extra steps the host stand actually cares about: how many guests and which table.
import { RestaurantBookingWidget } from "@periscaleai/booking-widget/restaurant";
<RestaurantBookingWidget
bookingApiBase="https://api.periscale.app/api/v1/business/website/booking"
reservationApiBase="https://api.periscale.app/api/v1/business/website/restaurant-booking"
apiKey="sb_live_xxx"
largePartyContactPhone="+65 8910 8835"
maxOnlinePartySize={8}
/>Flow
- Date — calendar of dates within
max_advance_booking_days. - Time — slot grid for the selected date.
- Party — chip selector (1–
maxOnlinePartySize). Guests above the cap see a "call us" CTA. - Table — only tables with
capacity >= partySizeand no overlapping non-cancelled reservation are shown. Grouped by section (Main, Patio, Bar, …) when the section field is set. - Details — name, phone, email, special requests.
- Confirm — confirmation screen with reservation ID, party size, and table number; confirmation email is sent (party size + table appended automatically).
The "My Reservations" tab works exactly like the generic widget's "My Bookings" — phone-number lookup, with party + table shown on each card.
Props
RestaurantBookingWidgetProps extends both BookingClientOptions (so apiKey flows through) and RestaurantClientBases.
| Prop | Type | Required | Description |
|---|---|---|---|
bookingApiBase | string | ✅ | Generic booking endpoint base. The widget reuses getConfig / getServices / getSlots from this. |
reservationApiBase | string | ✅ | Restaurant endpoint base — /tables/, /reservations/, /my-reservations/. |
apiKey | string | ✅ on third-party sites | sb_live_… widget key. Appended as ?api_key=… on every request. |
branchId | number | optional | Pin reservations to a specific branch. Default: backend picks the first active branch for your business. |
largePartyContactPhone | string | optional | Phone number shown on the party-size step for groups above the online cap. |
maxOnlinePartySize | number | optional, default 8 | Largest party that can be booked online. Anything bigger routes to the phone CTA. |
defaultTab | "reserve" | "my-reservations" | optional, default "reserve" | Which tab is active on first paint. |
className | string | optional | Extra Tailwind classes on the outer container. |
onBookingComplete | (reservation: Reservation) => void | optional | Fires after a successful reservation creation. Use for analytics or redirects. |
Sub-components
If you need a different shell but want to keep our pickers, every step is exported:
import {
PartySizePicker,
TablePicker,
ReservationDetailsForm,
ReservationConfirmation,
MyReservations,
createRestaurantBookingClient,
} from "@periscaleai/booking-widget/restaurant";Compose them with the building blocks (CalendarPicker, TimeSlotGrid, StepIndicator, TabBar).
Backend setup
The restaurant flavor needs the Periscale restaurant Django app enabled and:
- A
Branchwithis_active = true. - Several
Tablerows under that branch (withcapacityset;sectionis optional but improves the picker UX). - The
BookingConfigfor the business hasis_enabled = trueand weeklyBusinessAvailabilityrows for the days you accept reservations.
The widget calls these endpoints (under reservationApiBase):
| Method | Path | Purpose |
|---|---|---|
GET | /tables/?date=&start_time=&party_size=[&service_id=&branch_id=] | Tables available for a chosen slot + party size. |
POST | /reservations/ | Create a reservation (Appointment + RestaurantReservation sidecar). |
GET | /my-reservations/?phone= | Phone-number lookup. |
POST | /reservations/{id}/cancel/ | Customer-side cancel, respects cancellation_policy_hours. |
Confirmation email
The generic booking confirmation email is sent automatically. The restaurant app registers an email-extension callback so the body includes:
Party size: 4
Table: Patio 7These lines only appear when a RestaurantReservation is attached to the appointment — non-restaurant verticals get the plain template.