Booking Groups API
The Booking Groups API provides endpoints for creating and managing multi-activity booking groups. A booking group coordinates multiple bookings into a single unit with shared payment, notifications, and lifecycle management.
Overview
Booking groups allow combining multiple activities (potentially across different sites) into a single event. For example, a corporate team day might include Hohtogolf at 14:00 and Laserareena at 15:30, managed as one booking with a single invoice.
Key concepts:
- BookingGroup — coordinates multiple child bookings
- Child Booking — a standard booking linked to a group via
groupId - Group Status — derived from child booking states (DRAFT → HOLD → CONFIRMED → COMPLETED)
- Group Payment — single payment transaction covers all activities
Public Endpoints
Create Group Hold
Creates a booking group from a package with atomic holds for all activities. All holds share the same expiry time — if any activity can't be held, the entire operation rolls back.
POST /api/v1/booking-groups/hold
Request Body:
{
"packageId": "pkg_abc123",
"groupType": "PACKAGE",
"partySize": 20,
"customerName": "Acme Corp",
"customerEmail": "events@acme.com",
"customerPhone": "+358501234567",
"slotSelections": [
{
"productId": "p_hohtogolf",
"siteId": "s_redi",
"slotId": "slot_1400"
},
{
"productId": "p_laser",
"siteId": "s_redi",
"slotId": "slot_1530"
}
]
}
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
packageId | string | No | Package to create group from (null for custom) |
groupType | string | Yes | PACKAGE or CUSTOM |
partySize | number | Yes | Total group size (1-500) |
customerName | string | No | Customer or company name |
customerEmail | string | No | Customer email for confirmations |
customerPhone | string | No | Customer phone number |
slotSelections | array | No | Slot picks for each activity |
Response:
{
"id": "grp_xyz789",
"reference": "GRP-20260415-001",
"status": "HOLD",
"holdExpiresAt": "2026-04-15T14:15:00.000Z",
"bookings": [
{
"bookingId": "bk_001",
"status": "hold",
"slotId": "slot_1400",
"startsAt": "2026-04-15T14:00:00.000Z",
"endsAt": "2026-04-15T15:00:00.000Z"
},
{
"bookingId": "bk_002",
"status": "hold",
"slotId": "slot_1530",
"startsAt": "2026-04-15T15:30:00.000Z",
"endsAt": "2026-04-15T16:30:00.000Z"
}
]
}
Get Group Detail
GET /api/v1/booking-groups/:id
Returns group info with all child bookings and pricing.
Confirm Group
POST /api/v1/booking-groups/:id/confirm
Request Body:
{
"paymentMethod": "STRIPE"
}
| Field | Type | Required | Description |
|---|---|---|---|
paymentMethod | string | Yes | STRIPE, PAYTRAIL, INVOICE, or COMP |
For STRIPE, the response includes a stripeClientSecret for completing payment via Stripe Elements on the frontend. The bookings remain in HOLD until the Stripe webhook confirms payment.
For INVOICE and COMP, bookings are confirmed immediately.
Admin Endpoints
All admin endpoints require Bearer token authentication and manage_bookings permission.
List Booking Groups
GET /api/v1/admin/booking-groups
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by group status |
customerEmail | string | Search by customer email |
Create Booking Group
POST /api/v1/admin/booking-groups
Creates a group in DRAFT status. For custom groups, add bookings individually afterwards.
Get Booking Group
GET /api/v1/admin/booking-groups/:id
Update Booking Group
PATCH /api/v1/admin/booking-groups/:id
Update customer info, internal notes.
Delete Booking Group
DELETE /api/v1/admin/booking-groups/:id
Only DRAFT groups can be deleted.
Add Booking to Group
POST /api/v1/admin/booking-groups/:id/bookings
Request Body:
{
"productId": "p_hohtogolf",
"siteId": "s_redi",
"slotId": "slot_1400",
"partySize": 20
}
Only works for DRAFT groups. The product must belong to the specified site, and the slot must exist for the product.
Remove Booking from Group
DELETE /api/v1/admin/booking-groups/:id/bookings/:bookingId
Only works for DRAFT groups.
Create Group Hold
POST /api/v1/admin/booking-groups/:id/hold
Creates atomic holds for all bookings in the group with synchronized expiry. Fails entirely if any booking's slot has insufficient capacity.
Calculate Pricing
POST /api/v1/admin/booking-groups/:id/calculate-pricing
Calculates and applies pricing to the group. Returns:
{
"grandTotal": 1100.0,
"subtotal": 1100.0,
"taxTotal": 280.5,
"pricingMethod": "BUNDLE_PER_PERSON",
"allocations": [
{
"bookingId": "bk_001",
"allocatedSubtotal": 567.74,
"taxAmount": 144.77,
"allocatedTotal": 712.51
}
]
}
Confirm Group
POST /api/v1/admin/booking-groups/:id/confirm
Request Body:
{
"paymentMethod": "INVOICE",
"customTotal": 900.0,
"customPricingReason": "Returning customer discount"
}
| Field | Type | Required | Description |
|---|---|---|---|
paymentMethod | string | Yes | INVOICE, COMP, STRIPE, or PAYTRAIL |
customTotal | number | No | Override total (requires reason) |
customPricingReason | string | No | Required when customTotal is set |
Cancel All Bookings
POST /api/v1/admin/booking-groups/:id/cancel
Request Body:
{
"reason": "Client budget cut"
}
Cancels all non-terminal bookings in the group and releases capacity.
Group Status Lifecycle
DRAFT → HOLD → CONFIRMED → COMPLETED
↓ ↓ ↓
EXPIRED EXPIRED PARTIALLY_CANCELED → CANCELED
| Status | Meaning |
|---|---|
DRAFT | Group created, activities being added |
HOLD | All bookings have active holds |
CONFIRMED | Payment processed, all bookings confirmed |
PARTIALLY_CANCELED | Some bookings cancelled, others still active |
CANCELED | All bookings cancelled |
COMPLETED | All bookings completed or in terminal state |
EXPIRED | Hold expired before confirmation |
Group status is derived from child booking states and updates automatically when any child booking's status changes.
Payment Flow
Invoice Payment
- Admin creates group and adds activities
- Admin calculates pricing
- Admin creates holds (atomic)
- Admin confirms with
paymentMethod: "INVOICE" - All bookings confirmed immediately, payment transaction created as PENDING
- Confirmation email sent to customer
Stripe Payment (Customer Flow)
- Customer selects package, picks slots, enters contact info
- System creates atomic holds
- Customer pays via Stripe checkout
- Stripe webhook fires → all bookings confirmed automatically
- Confirmation email sent
Complimentary (Comp)
Confirms all bookings with zero payment. Useful for partner events.