Skip to main content

Resource Add-ons API

The Resource Add-ons API allows you to manage time-based bookable resources such as party rooms, VIP lounges, and equipment rentals that can be added to bookings.

Overview

Resource add-ons differ from regular add-ons in that they:

  • Have specific time windows (before, during, or after the main activity)
  • May have limited capacity and availability
  • Support various duration and pricing modes
  • Are tracked as resource allocations in the system

Get Resource Addon Configurations

Retrieve available resource addon configurations for a product.

GET /api/v1/availability/resource-addons

Query Parameters

ParameterTypeRequiredDescription
product_idstringYesProduct identifier

Example Request

GET /api/v1/availability/resource-addons?product_id=p_laser_tag
x-tenant-id: t_demo

Response

Status: 200 OK

{
"configs": [
{
"id": "rac_party_room",
"productId": "p_laser_tag",
"addonId": "addon_party_room",
"addonName": "Party Room",
"addonDescription": "Private party room for your group celebration",
"resourceId": "res_party_room_1",
"resourceName": "Party Room A",
"durationMode": "SELECTABLE",
"durationOptions": [30, 60, 90],
"minDurationMins": 30,
"maxDurationMins": 90,
"defaultDurationMins": 60,
"pricingMode": "PER_HOUR",
"flatPrice": null,
"hourlyRate": 50.0,
"perPersonPrice": null,
"basePrice": null,
"allowBefore": true,
"allowAfter": true,
"allowDuring": false,
"bufferBeforeMins": 15,
"bufferAfterMins": 15,
"maxCapacity": 20,
"isActive": true
}
]
}

Response Fields

FieldTypeDescription
idstringUnique configuration identifier
productIdstringAssociated product
addonIdstringAssociated addon
addonNamestringDisplay name of the addon
addonDescriptionstringDescription of the addon
resourceIdstringAssociated resource
resourceNamestringDisplay name of the resource
durationModestringOne of: FIXED, SELECTABLE, CUSTOM
durationOptionsnumber[]Available durations for SELECTABLE mode (minutes)
minDurationMinsnumberMinimum duration for CUSTOM mode
maxDurationMinsnumberMaximum duration for CUSTOM mode
defaultDurationMinsnumberDefault duration
pricingModestringOne of: FLAT, PER_HOUR, PER_PERSON, COMPOSITE
flatPricenumberPrice for FLAT mode
hourlyRatenumberHourly rate for PER_HOUR mode
perPersonPricenumberPer-person price for PER_PERSON and COMPOSITE modes
basePricenumberBase fee for COMPOSITE mode
allowBeforebooleanCan be booked before the activity
allowAfterbooleanCan be booked after the activity
allowDuringbooleanCan be booked during the activity
bufferBeforeMinsnumberBuffer time before activity (minutes)
bufferAfterMinsnumberBuffer time after activity (minutes)
maxCapacitynumberMaximum capacity for the resource
isActivebooleanWhether the configuration is active

Get Available Time Windows

Retrieve available time windows for a resource addon configuration.

GET /api/v1/availability/resource-addons/:configId/windows

Path Parameters

ParameterTypeRequiredDescription
configIdstringYesResource addon configuration ID

Query Parameters

ParameterTypeRequiredDescription
activity_starts_atstringYesActivity start time (ISO 8601)
activity_ends_atstringYesActivity end time (ISO 8601)
party_sizenumberNoNumber of participants (default: 1)
exclude_booking_idstringNoBooking ID to exclude from conflicts

Example Request

GET /api/v1/availability/resource-addons/rac_party_room/windows?activity_starts_at=2025-10-15T14:00:00Z&activity_ends_at=2025-10-15T15:00:00Z&party_size=8
x-tenant-id: t_demo

Response

Status: 200 OK

{
"configId": "rac_party_room",
"windows": [
{
"position": "BEFORE",
"startsAt": "2025-10-15T12:45:00.000Z",
"endsAt": "2025-10-15T13:45:00.000Z",
"durationMins": 60,
"price": 50.0,
"isAvailable": true
},
{
"position": "AFTER",
"startsAt": "2025-10-15T15:15:00.000Z",
"endsAt": "2025-10-15T16:15:00.000Z",
"durationMins": 60,
"price": 50.0,
"isAvailable": true
},
{
"position": "AFTER",
"startsAt": "2025-10-15T15:15:00.000Z",
"endsAt": "2025-10-15T16:45:00.000Z",
"durationMins": 90,
"price": 75.0,
"isAvailable": false
}
]
}

Response Fields

FieldTypeDescription
configIdstringConfiguration identifier
windowsarrayArray of available time windows
windows[].positionstringOne of: BEFORE, DURING, AFTER
windows[].startsAtstringWindow start time (ISO 8601)
windows[].endsAtstringWindow end time (ISO 8601)
windows[].durationMinsnumberDuration in minutes
windows[].pricenumberCalculated price for this window
windows[].isAvailablebooleanWhether the window is available

Add Resource Addons to Booking Hold

Add resource addon selections when creating or updating a booking hold.

POST /api/v1/bookings/:bookingId/resource-addons

Path Parameters

ParameterTypeRequiredDescription
bookingIdstringYesBooking identifier

Request Body

{
"selections": [
{
"configId": "rac_party_room",
"startsAt": "2025-10-15T12:45:00.000Z",
"endsAt": "2025-10-15T13:45:00.000Z",
"durationMins": 60
}
]
}

Request Fields

FieldTypeRequiredDescription
selectionsarrayYesArray of resource selections
selections[].configIdstringYesResource addon configuration ID
selections[].startsAtstringYesStart time (ISO 8601)
selections[].endsAtstringYesEnd time (ISO 8601)
selections[].durationMinsnumberYesDuration in minutes

Response

Status: 200 OK

{
"success": true,
"allocations": [
{
"id": "alloc_abc123",
"configId": "rac_party_room",
"resourceId": "res_party_room_1",
"startsAt": "2025-10-15T12:45:00.000Z",
"endsAt": "2025-10-15T13:45:00.000Z",
"durationMins": 60,
"price": 50.0,
"status": "HELD"
}
],
"pricingUpdate": {
"resourceAddonTotal": 50.0,
"newSubtotal": 150.0,
"newTaxTotal": 37.5,
"newGrandTotal": 187.5
}
}

Get Booking Resource Addons

Retrieve resource addons for an existing booking.

GET /api/v1/bookings/:bookingId/resource-addons

Path Parameters

ParameterTypeRequiredDescription
bookingIdstringYesBooking identifier

Example Request

GET /api/v1/bookings/bk_123/resource-addons
x-tenant-id: t_demo

Response

Status: 200 OK

{
"addons": [
{
"bookingAddonId": "ba_abc123",
"allocationId": "alloc_abc123",
"addonId": "addon_party_room",
"addonName": "Party Room",
"resourceId": "res_party_room_1",
"resourceName": "Party Room A",
"startsAt": "2025-10-15T12:45:00.000Z",
"endsAt": "2025-10-15T13:45:00.000Z",
"durationMins": 60,
"totalPrice": 50.0,
"status": "CONFIRMED"
}
]
}

Modify Booking Resource Addons

Add or remove resource addons from an existing confirmed booking.

PATCH /api/v1/bookings/:bookingId/resource-addons

Path Parameters

ParameterTypeRequiredDescription
bookingIdstringYesBooking identifier

Request Body

{
"add": [
{
"configId": "rac_vip_lounge",
"startsAt": "2025-10-15T15:15:00.000Z",
"endsAt": "2025-10-15T16:15:00.000Z",
"durationMins": 60
}
],
"remove": ["alloc_xyz789"]
}

Request Fields

FieldTypeRequiredDescription
addarrayNoResource addon selections to add
removestring[]NoAllocation IDs to remove

Response

Status: 200 OK

{
"success": true,
"addons": [...],
"pricingUpdate": {
"resourceAddonTotal": 75.00,
"newSubtotal": 175.00,
"newTaxTotal": 43.75,
"newGrandTotal": 218.75
},
"paymentAdjustment": {
"type": "ADDITIONAL_CHARGE",
"amount": 31.25,
"paymentRequired": true
}
}

Resolve Resource Addon Conflicts

When confirming a booking, resource conflicts may occur if the requested time windows are no longer available. This endpoint resolves conflicts.

POST /api/v1/bookings/:bookingId/resource-addons/resolve-conflict

Path Parameters

ParameterTypeRequiredDescription
bookingIdstringYesBooking identifier

Request Body

{
"action": "SELECT_ALTERNATIVE",
"allocationId": "alloc_abc123",
"newStartsAt": "2025-10-15T16:15:00.000Z",
"newEndsAt": "2025-10-15T17:15:00.000Z",
"expected_pricing_hash": "hash_xyz",
"payment_mode": "stripe"
}

Action Types

ActionDescription
SELECT_ALTERNATIVEChoose an alternative time window
PROCEED_WITHOUTContinue without the conflicted resource addons

Request Fields

FieldTypeRequiredDescription
actionstringYesResolution action
allocationIdstringNoAllocation to modify (for SELECT_ALTERNATIVE)
newStartsAtstringNoNew start time (for SELECT_ALTERNATIVE)
newEndsAtstringNoNew end time (for SELECT_ALTERNATIVE)
releaseAllocationIdsstring[]NoAllocations to release (for PROCEED_WITHOUT)
expected_pricing_hashstringYesPricing hash for verification
payment_modestringYesPayment mode: stripe or poa

Response

Status: 200 OK

{
"status": "CONFIRMED",
"paymentIntent": {
"clientSecret": "pi_xxx_secret_yyy"
}
}

Conflict Response Format

When booking confirmation encounters resource conflicts, the response includes conflict details:

{
"resourceConflict": {
"conflicts": [
{
"allocationId": "alloc_abc123",
"addonId": "addon_party_room",
"addonName": "Party Room",
"resourceId": "res_party_room_1",
"resourceName": "Party Room A",
"requestedStartsAt": "2025-10-15T12:45:00.000Z",
"requestedEndsAt": "2025-10-15T13:45:00.000Z",
"conflictReason": "Resource already booked for this time slot",
"alternativeWindows": [
{
"position": "BEFORE",
"startsAt": "2025-10-15T11:30:00.000Z",
"endsAt": "2025-10-15T12:30:00.000Z",
"durationMins": 60,
"price": 50.0,
"isAvailable": true
}
]
}
],
"options": [
{
"action": "SELECT_ALTERNATIVE",
"description": "Choose alternative time slots"
},
{
"action": "PROCEED_WITHOUT",
"description": "Continue without these resources"
}
],
"pricingHash": "hash_abc123"
}
}

Error Responses

400 Bad Request

{
"statusCode": 400,
"message": "Invalid time window: starts_at must be before ends_at",
"error": "Bad Request"
}

404 Not Found

{
"statusCode": 404,
"message": "Resource addon configuration not found",
"error": "Not Found"
}

409 Conflict

{
"statusCode": 409,
"message": "Resource is not available for the requested time window",
"error": "Conflict"
}

422 Unprocessable Entity

{
"statusCode": 422,
"message": "Duration 45 minutes is not allowed for this configuration",
"error": "Unprocessable Entity"
}

Duration & Pricing Modes

Duration Modes

Each resource addon configuration has a durationMode that controls how the customer selects a duration.

FIXED

A single immutable duration set by the admin. The booking request must match exactly.

Config fieldTypeDescription
fixedDurationMinsinteger (>= 1)The required duration in minutes

Example: A party room is always 120 minutes.

SELECTABLE

The customer picks from a predefined list of allowed durations.

Config fieldTypeDescription
selectableDurationsinteger[]Allowed durations in minutes

Example: [30, 60, 90, 120] — the customer picks one.

CUSTOM

The customer enters any duration within a min/max range, optionally constrained to a step increment.

Config fieldTypeDescription
minDurationMinsinteger (>= 1)Minimum allowed duration
maxDurationMinsinteger (>= 1)Maximum allowed duration
durationStepMinsinteger (>= 1, default 30)Increment step

Example: min 30, max 180, step 15 produces valid values 30, 45, 60, …, 180.

Validation: The API rejects requests with a duration that does not satisfy the mode's constraints (exact match for FIXED, membership in the list for SELECTABLE, range and step for CUSTOM).


Pricing Modes

Each configuration has a pricingMode that determines how the price is calculated. The calculation runs inside ResourceAddonAvailabilityService.calculatePrice().

FLAT

Constant price regardless of duration or party size.

Config fieldRequiredFormula
flatPriceYesprice = flatPrice

Example: Party room = €50 whether booked for 1 hour or 2 hours.

PER_HOUR

Price scales linearly with duration.

Config fieldRequiredFormula
perHourPriceYesprice = perHourPrice × (durationMins / 60)

Example: Boat rental at €25/hour for 90 minutes = €37.50.

PER_PERSON

Price scales with party size, independent of duration.

Config fieldRequiredFormula
perPersonPriceYesprice = perPersonPrice × partySize

Example: Guide service at €20/person for 4 people = €80.

COMPOSITE

Base fee plus a per-person surcharge.

Config fieldRequiredFormula
basePriceYesprice = basePrice + (perPersonPrice × partySize)
perPersonPriceYes

Example: €30 setup fee + €15/person for 4 people = €30 + €60 = €90.


Timing Constraints

Each configuration also controls when the resource can be booked relative to the main activity.

FieldTypeDefaultDescription
allowBeforebooleantrueCan be booked before the activity starts
allowDuringbooleantrueCan be booked overlapping with the activity
allowAfterbooleantrueCan be booked after the activity ends
bufferMinsBeforeinteger0Setup buffer before the activity (minutes)
bufferMinsAfterinteger0Cleanup buffer after the activity (minutes)

Example: A party room that needs 30 min setup and 15 min cleanup would use bufferMinsBefore: 30, bufferMinsAfter: 15.


Resource Types

Each resource has a resourceType that categorizes it. This affects admin UI grouping and reporting but does not change booking logic.

TypeDescriptionExamples
ROOMPhysical spaces with capacity limits, often requiring setup/cleanup timeParty Room A, Conference Hall
EQUIPMENTItems needed for activities, can be shared or exclusiveLaser Tag Set 1, Cooking Station
VEHICLEWatercraft, land vehicles, or other transportKayak #3, Tour Bus, Pontoon Boat
STAFFPersonnel required for activities, supports skill-based schedulingInstructor Sarah, Safety Supervisor
OTHERConsumables, inventory, or anything that doesn't fit the aboveSafety Waiver, Equipment Cleaning Kit

Shared Modes

Each resource has a sharedMode that controls how its capacity is consumed across concurrent bookings.

EXCLUSIVE

The resource can only be used by one booking at a time. When booked, the resource is fully locked for that time window.

Use case: A private party room that cannot be double-booked.

SHARED

The resource can be used by multiple bookings simultaneously, up to its base capacity. Each booking consumes a portion of the capacity.

Use case: A set of laser tag vests shared across groups running at the same time.

POOLED

The resource is part of a capacity pool. Bookings draw from the pool's total capacity rather than from individual resource units.

Use case: General admission slots where the total venue capacity is shared across all concurrent bookings.

See the Catalog Guide for admin UI configuration of resource types and shared modes.

Notes

  • Resource addon allocations are held when added to a booking hold
  • Held allocations expire when the booking hold expires
  • Confirmed allocations become permanent when the booking is confirmed
  • Resource availability is checked in real-time during confirmation
  • Price changes between hold and confirmation trigger pricing hash verification