Group / tiered pricing applies an automatic percent discount when a
single reservation has quantity above a threshold. Useful for
encouraging tour groups, schools, or corporate outings to book together
instead of as individuals.
How it works
Each pricing tier has a group_pricing_tiers JSONB column. The value
is an array of objects:
[
{ "min_quantity": 5, "discount_percent": 10 },
{ "min_quantity": 10, "discount_percent": 20 },
{ "min_quantity": 25, "discount_percent": 30 }
]
When the pricing engine calculates the cost for a reservation:
- It computes the per-unit base cost (hourly × hours, or daily × days, etc.)
- It looks up the highest applicable threshold (
min_quantity ≤ reservation.quantity) - Applies that threshold's
discount_percentto the per-unit cost - Multiplies by quantity for the total
Examples (with hourly $10, 2-hour rental):
| Quantity | Threshold applied | Per-unit | Total |
|---|---|---|---|
| 1 | none | $20 | $20 |
| 4 | none | $20 | $80 |
| 5 | 10% off | $18 | $90 |
| 9 | 10% off | $18 | $162 |
| 10 | 20% off | $16 | $160 |
| 25 | 30% off | $14 | $350 |
Note quantity=10 is cheaper total than quantity=9 — that's intentional to incentivize larger groups.
Setting it up
Today the dashboard pricing UI doesn't expose the JSON column directly. Set via SQL or the database editor:
UPDATE reservation_pricing_tiers
SET group_pricing_tiers = '[
{"min_quantity": 5, "discount_percent": 10},
{"min_quantity": 10, "discount_percent": 20}
]'::jsonb
WHERE id = '<your-tier-id>';
A native "Group discounts" UI on the pricing page is on the roadmap.
Discount stacking rules
- Highest threshold wins. The 30% threshold replaces the 20% — they don't add.
- Group discount stacks with promo codes. A 10% group discount plus a 5% promo code = ~14.5% net (multiplicative). Most operators prefer to see the promo code as the smaller of the two and let customers stack.
- Group discount applies before tax. The discounted subtotal is what tax is calculated on.
Where it shows
On the booking detail
The financial summary shows the per-unit base cost reflecting the discount. There's no explicit "Group discount: -X%" line — the discount is folded into the base rate.
This is a small UX gap. To make the discount visible, add a custom adjustment_reason via SQL or update the pricing snapshot manually.
On the public booking page
The public page shows the estimated total reflecting the group discount automatically. Customers don't see the percent off explicitly, but they see a lower per-bike total.
On the receipt
Same — the receipt shows the discounted base cost, not the original. For "Group discount of $X" callout on receipts, that's a roadmap feature.
Edge cases
Reservation with quantity = 1 but with group thresholds set
The system looks for the highest threshold ≤ quantity. With quantity=1, no threshold applies. Customer pays full per-unit price.
Negative discount values
The schema doesn't validate, but the pricing engine clamps to a max of 100% off. A negative value would error or be ignored — don't enter negatives.
Threshold larger than your max bike inventory
If you have 10 bikes total and set a 25-bike threshold, no one can ever hit it. The threshold is just unreachable — no harm done.
Same threshold appearing twice
Last one wins (JSON object keys / array iteration). Don't do this. Each threshold should be unique.
Reporting
Track how often group discounts apply:
SELECT
date_trunc('month', pickup_at) AS month,
count(*) FILTER (WHERE quantity >= 5) AS group_bookings,
sum(quantity) FILTER (WHERE quantity >= 5) AS total_riders,
sum(total_cents) FILTER (WHERE quantity >= 5) / 100.0 AS group_revenue
FROM reservations
WHERE subaccount_id = '<your-subaccount-id>'
AND pickup_at >= '2026-01-01'
AND status NOT IN ('cancelled', 'no_show', 'expired')
GROUP BY 1
ORDER BY 1 DESC;
If group bookings are growing month-over-month, your pricing strategy is working.
Common configurations
Family-friendly shop
[
{ "min_quantity": 4, "discount_percent": 10 }
]
Encourages families to book together.
Tour shop
[
{ "min_quantity": 6, "discount_percent": 10 },
{ "min_quantity": 12, "discount_percent": 15 },
{ "min_quantity": 20, "discount_percent": 25 }
]
Multiple tiers for different tour group sizes.
School / corporate booking
[
{ "min_quantity": 10, "discount_percent": 20 },
{ "min_quantity": 50, "discount_percent": 35 }
]
Bigger jumps to incentivize large bookings.
What's NOT in group pricing
- Quantity-based pricing per add-on. Add-ons are flat per-unit; groups don't get a discount on their helmet purchases.
- Time-based group thresholds. A "weekend group of 10" doesn't get a special tier on top of the existing tiered pricing.
- Individual rider name discounts. All riders in a group reservation share one rate.
These are workarounds via separate tiers, but not natively in this column.