A "group booking" in Shop Rentals is a reservation with quantity > 1
— same pickup time, same return time, multiple bikes. Common for
families, tour groups, or corporate outings.
The data model
A single reservations row covers all bikes in the group:
quantity = 4— four bikes in this bookingvehicle_uuid IS NULL— at the model level, not per-vehiclevehicle_model_id— the model they're all rentingtotal_centsreflects the per-unit cost × quantity (with any group discount)
At assignment time, individual vehicles are linked via
reservation_assignments rows, one per bike. So a quantity=4 booking
can have 4 assigned vehicles, all on the same reservation.
Creating a group booking
Via the public booking page
The public page currently supports quantity = 1 only. Group bookings
must be created operator-side.
Via the operator new-booking flow
/dashboard/shop-rentals/new has a quantity selector (1–20). Pick the
quantity, location, model, dates → confirm.
Via the walk-in wizard
The wizard creates one reservation per session. For groups arriving at the counter, either:
- Run the wizard
quantitytimes (one reservation per rider) — easier for separate billing - Use the new-booking flow with
quantity > 1— easier for one reservation covering all bikes (preferred for tours)
Group discounts
Pricing tiers support quantity-based discounts via the
group_pricing_tiers JSON column:
[
{ "min_quantity": 5, "discount_percent": 10 },
{ "min_quantity": 10, "discount_percent": 20 }
]
A quantity = 7 booking gets the 10% threshold; quantity = 12 gets
20%. The pricing engine picks the highest applicable threshold and
applies it as a multiplier on the per-unit base cost. See
Group / tiered pricing for
the full reference.
Per-rider waivers
For a 4-bike group, you may want 4 separate waivers so each rider signs individually with their typed name.
Issue per-rider waivers
On the booking detail page, in the Agreements & Waivers panel:
- Click Issue signing link for your active template
- The first link is
rider_index = 0, generic for the lead booker - To issue per-rider, use the API:
POST /api/reservations/[id]/agreements
{
"templateId": "...",
"isPerRider": true,
"riderIndex": 1
}
Repeat for riderIndex: 2, 3, 4 etc. Each gets a separate token and a
separate signed_agreements row.
How customers see them
The signing page shows a "Rider X of Y" badge when is_per_rider=true
so the rider knows which slot they're filling. The lead booker sends
each link to the corresponding rider via SMS, AirDrop, or — at the
counter — a printed QR code per rider.
What you get back
After all rider signatures arrive, the booking detail page shows N signed-agreement rows, one per rider, with each rider's typed name and timestamp.
Operational tips
Counter group flow
- Create the group booking with
quantity = 4 - Issue 4 per-rider waiver links
- Print them as 4 QR codes on a single sheet
- Hand each rider their QR code
- Wait for all 4 to sign on their phones
- Verify the booking detail shows 4 signed waivers
- Hand over the bikes
This whole flow takes about 5-7 minutes once everyone's at the counter.
Mixed-rider groups (parents + minors)
Minors typically need a parent or guardian signature. Two patterns:
- Single template with parent-signed clause — the parent signs once on behalf of all minors in the group. Track minor names in a custom field on the agreement template.
- Separate templates per rider type — adult template for adults,
minor-with-guardian template for minors. Issue different templates
per
riderIndex.
Damage handling on group bookings
A damage event ties to the group reservation, not to a specific rider.
If you can identify which rider caused it, capture that in the
description field of the service log entry. The damage charge adds to
the group reservation's total.
If you want per-rider liability, you'll need a contractual mechanism outside the system (e.g., the lead booker is the one financially responsible per your policy).
What's not in group bookings
- Per-rider pricing differences — all riders in a group reservation pay the same unit rate. You can't have rider 1 at adult pricing and rider 2 at child pricing in one reservation. Make separate bookings if you need this.
- Per-rider time windows — all riders share
pickup_atandreturn_at. If a tour has staggered start times per rider, each rider needs a separate booking. - Per-rider add-ons — add-ons are at the reservation level, not
per-rider. If 2 of 4 riders want helmets and 2 don't, configure the
group reservation with
quantity=2for helmet, even thoughquantity=4for the bikes.
Reporting group bookings
SELECT
date_trunc('week', pickup_at) AS week,
count(*) FILTER (WHERE quantity > 1) AS group_bookings,
sum(quantity) FILTER (WHERE quantity > 1) AS group_riders,
avg(total_cents / quantity) / 100.0 AS avg_per_rider_revenue
FROM reservations
WHERE pickup_at >= '2026-05-01'
GROUP BY 1
ORDER BY 1 DESC;
Track this metric — group bookings are typically your highest-margin segment because the discount tier is small relative to the operational efficiency of one transaction covering many bikes.