Pool Booking API
Pool Allocation Contracts
Configure interchangeable resource pools, submit requested quantities, and predict quote snapshots exactly
This page collects the pool-booking rules that are otherwise spread across the resource-type DTOs, reservation writes, public booking service, and pricing service.
bookingMode awarerequestedQuantity awareOrganisation public bookingServer-derived pricingCore Field Semantics
| Field | Meaning | Important rule |
|---|---|---|
bookingMode | How the resource type may be booked. | SINGLE_RESOURCE, POOL, or BOTH. |
quantity | Capacity requested within each assigned concrete resource. | Pool allocation currently requires quantity = 1. |
requestedQuantity | Number of concrete resources the allocator should assign. | Defaults to 1 when omitted. |
resourceCount | Number of resources actually assigned to the reservation. | Used in pricing snapshots. |
resourceIds | Explicit concrete resource selection. | All ids must belong to one resource type. |
bookingMode values
SINGLE_RESOURCE: rejectsrequestedQuantity > 1POOL: allows abstract resource-type booking and automatic allocationBOTH: allows either explicit resource ids or abstract pool allocation
Configure The Resource Type
Pool behavior starts at POST /api/resource-types and
PATCH /api/resource-types/:id.
Relevant fields:
bookingModepricingEnabledpaymentRequiredpriceAmountpriceCurrencyminBookingDurationbufferTime
Example:
curl -X POST "$PRIMECAL_API/api/resource-types" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Archery lane",
"bookingMode": "POOL",
"minBookingDuration": 60,
"bufferTime": 15,
"pricingEnabled": true,
"paymentRequired": true,
"priceAmount": 1999,
"priceCurrency": "usd"
}'
Internal Reservation Payloads
Use POST /api/reservations with resourceTypeId plus requestedQuantity
when the client wants automatic allocation.
Automatic pool allocation
{
"resourceTypeId": 3,
"requestedQuantity": 2,
"startTime": "2026-07-01T09:00:00.000Z",
"endTime": "2026-07-01T10:00:00.000Z",
"notes": "Need two adjacent lanes"
}
Behavior:
- allocator loads active resources of that type in deterministic order
- each candidate is inspected for reservation and blocking-calendar conflicts
- the first
requestedQuantityavailable resources are assigned
Explicit multi-resource selection
{
"resourceTypeId": 3,
"resourceIds": [5, 6],
"startTime": "2026-07-01T09:00:00.000Z",
"endTime": "2026-07-01T10:00:00.000Z"
}
Behavior:
requestedQuantityis derived from the number of explicitresourceIdswhen omitted- all selected resources must belong to the same resource type
quantity > 1is rejected when more than one concrete resource is selected
Update limitation
PATCH /api/reservations/:id currently rejects changing requestedQuantity.
Reallocation is not supported as part of the update flow yet.
Public Booking Payloads
Organisation booking catalogue
GET /api/public/booking/organisations/:slug
Resource types expose pool-relevant fields directly:
{
"resourceTypes": [
{
"id": 31,
"name": "Signature massage",
"bookingMode": "POOL",
"pricingEnabled": true,
"paymentRequired": true,
"paymentAvailable": true,
"priceAmount": 12500,
"priceCurrency": "eur"
}
]
}
Aggregated availability for pool booking
GET /api/public/booking/organisations/:slug/availability
Query:
date: requiredYYYY-MM-DDresourceTypeId: required positive intresourceId: optional for specific-resource moderequestedQuantity: optional positive int
When no resourceId is provided, PrimeCal returns aggregated slots:
{
"mode": "any",
"resourceType": {
"id": 31,
"bookingMode": "POOL"
},
"resources": [
{ "id": 41, "name": "Room A", "capacity": 1 },
{ "id": 42, "name": "Room B", "capacity": 1 }
],
"slots": [
{
"startTime": "2026-07-01T09:00:00.000Z",
"endTime": "2026-07-01T10:30:00.000Z",
"available": true,
"availableQuantity": 2,
"resourceIds": [41, 42]
}
]
}
availableQuantity is the number of currently assignable resources for that
slot, not an arbitrary client-side counter.
Organisation public reserve payload
POST /api/public/booking/organisations/:slug/reserve
{
"resourceTypeId": 3,
"requestedQuantity": 2,
"quantity": 1,
"startTime": "2026-07-01T09:00:00.000Z",
"endTime": "2026-07-01T10:00:00.000Z",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com",
"customerPhone": "+3612345678"
}
Representative response:
{
"reservationId": 77,
"resourceTypeId": 3,
"requestedQuantity": 2,
"assignedResourceIds": [5, 6],
"assignedResources": [
{ "id": 5, "name": "Lane 1" },
{ "id": 6, "name": "Lane 2" }
],
"status": "pending_payment",
"paymentStatus": "pending",
"requiresPayment": true,
"quotedAmount": 3998,
"quotedCurrency": "usd"
}
Legacy token route limitation
POST /api/public/booking/:token/reserve stays single-resource only.
requestedQuantity > 1 is rejected for token routes.
Pricing Mathematics
The pricing service currently computes the reservation snapshot as:
quotedUnitAmount = priceAmount
quotedTotalAmount = round_half_even(priceAmount * resourceCount * durationHours)
quotedCurrency = normalized lowercase Stripe currency
Important details:
resourceCountis the assigned resource count, which matchesrequestedQuantityfor a successful pool allocationdurationHoursis calculated fromendTime - startTimeusing arbitrary precision decimal arithmetic
MCP Pool Tools
PrimeCal also exposes pool-booking operations through MCP for agent workflows.
check_pool_capacity
Accepted fields:
poolIdorresourceTypeIdstartTimeendTimerequestedQuantityorquantityuserTimezone
Representative response fields:
canAccommodateavailableResourceCountavailableQuantitynextAvailableSlot
simulate_pool_pricing
Accepted fields:
poolIdorresourceTypeIdrequestedQuantityorquantitystartTimeandendTime, ordurationHoursuserTimezone
Representative response fields:
quotedUnitAmountquotedTotalAmountquotedCurrencytotalPricerequiredDeposit
When a dedicated deposit configuration is not present on the resource type, the
tool returns a zero-value requiredDeposit in the quote currency.
- when
pricingEnabledisfalse, the quote is forced to0 / 0 / null paymentRequiredmirrors the resource type and is snapshotted onto the reservation aspaymentRequiredSnapshot
Example:
| Input | Value |
|---|---|
priceAmount | 12500 |
resourceCount | 2 |
durationHours | 1.5 |
Result:
| Field | Value |
|---|---|
quotedUnitAmount | 12500 |
quotedTotalAmount | 37500 |
quotedCurrency | eur |
Errors and Edge Cases
Pool capacity conflict
If PrimeCal cannot allocate enough free resources, the allocator throws a conflict payload like:
{
"errorCode": "POOL_CAPACITY_CONFLICT",
"message": "Concurrent capacity conflict.",
"availableResourceCount": 1,
"requestedQuantity": 2,
"partialAllocationBlocked": true
}
Other common validation failures
requestedQuantity < 1:400SINGLE_RESOURCEwithrequestedQuantity > 1:400- selected resources from multiple resource types:
400 - public token route with
requestedQuantity > 1:400 requestedQuantitygreater than the pool size:400
Screenshot And MCP Targets
Use these exact placeholder paths in docs automation and manual MCP evidence:
| Surface | File |
|---|---|
| User guide admin state | docs/assets/user-guide/enterprise-reservation-system/admin-resource-type-booking-mode-pool.png |
| User guide public availability | docs/assets/user-guide/enterprise-reservation-system/customer-pool-booking-availability-grid.png |
| User guide public payment step | docs/assets/user-guide/enterprise-reservation-system/customer-pool-booking-payment-step.png |
| MCP availability evidence | docs/assets/agents/enterprise-reservations/mcp-pool-availability-response.png |
| MCP create evidence | docs/assets/agents/enterprise-reservations/mcp-pool-booking-create-response.png |