Skip to main content

Availability API

The Availability API allows you to query available time slots for products and retrieve add-ons associated with products.

Query Available Slots

Retrieve available time slots for a specific product within a date range.

GET /api/v1/availability

Query Parameters

ParameterTypeRequiredDescription
product_idstringYesProduct identifier
site_idstringYesSite identifier
date_fromstringYesStart date (YYYY-MM-DD format)
date_tostringNoEnd date (YYYY-MM-DD format). Defaults to date_from if not provided
party_sizenumberNoNumber of participants (default: 1)
include_labelsbooleanNoInclude slot label codes in response (default: true)

Example Request

GET /api/v1/availability?product_id=p_demo&site_id=s_demo&date_from=2025-10-10&date_to=2025-10-17&party_size=4&include_labels=true
x-tenant-id: t_demo

Response

Status: 200 OK

{
"request_id": "req_abc123",
"productId": "p_demo",
"siteId": "s_demo",
"dateFrom": "2025-10-10",
"dateTo": "2025-10-17",
"partySize": 4,
"slots": [
{
"slotId": "slot_123",
"startsAt": "2025-10-10T10:00:00.000Z",
"endsAt": "2025-10-10T11:00:00.000Z",
"availableCapacity": 12,
"totalCapacity": 16,
"labelCodes": ["adults_only"],
"pricingPreview": {
"subtotal": 80.0,
"taxTotal": 20.0,
"grandTotal": 100.0,
"currency": "EUR"
}
},
{
"slotId": "slot_124",
"startsAt": "2025-10-10T11:30:00.000Z",
"endsAt": "2025-10-10T12:30:00.000Z",
"availableCapacity": 8,
"totalCapacity": 16,
"labelCodes": [],
"pricingPreview": {
"subtotal": 80.0,
"taxTotal": 20.0,
"grandTotal": 100.0,
"currency": "EUR"
}
}
]
}

Response Fields

FieldTypeDescription
request_idstringUnique request identifier
productIdstringProduct identifier from request
siteIdstringSite identifier from request
dateFromstringStart date from request
dateTostringEnd date from request
partySizenumberParty size from request
slotsarrayArray of available slots
slots[].slotIdstringUnique slot identifier
slots[].startsAtstringSlot start time (ISO 8601)
slots[].endsAtstringSlot end time (ISO 8601)
slots[].availableCapacitynumberAvailable capacity (total - booked)
slots[].totalCapacitynumberTotal capacity for this slot
slots[].labelCodesstring[]Array of label codes (e.g., "adults_only", "toddler_friendly")
slots[].pricingPreviewobjectEstimated pricing for party size
slots[].pricingPreview.subtotalnumberSubtotal before tax
slots[].pricingPreview.taxTotalnumberTax amount
slots[].pricingPreview.grandTotalnumberTotal price including tax
slots[].pricingPreview.currencystringCurrency code (e.g., "EUR")

Empty Response

If no slots are available, the response will still be 200 OK with an empty slots array:

{
"request_id": "req_abc123",
"productId": "p_demo",
"siteId": "s_demo",
"dateFrom": "2025-10-10",
"dateTo": "2025-10-17",
"partySize": 4,
"slots": []
}

Error Responses

400 Bad Request - Invalid parameters

{
"error": "VALIDATION_ERROR",
"message": "Missing required parameter: product_id",
"code": 400
}

404 Not Found - Product or site not found

{
"error": "NOT_FOUND",
"message": "Product or site not found",
"code": 404
}

Get Product Add-ons

Retrieve available add-ons for a specific product.

GET /api/v1/availability/products/:productId/addons

Path Parameters

ParameterTypeRequiredDescription
productIdstringYesProduct identifier

Example Request

GET /api/v1/availability/products/p_demo/addons
x-tenant-id: t_demo

Response

Status: 200 OK

{
"product_id": "p_demo",
"addons": [
{
"id": "addon_123",
"name": "Party Room",
"description": "Private party room for 2 hours",
"addonType": "resource",
"mode": "yesno",
"priceType": "fixed",
"price": 50.0,
"currency": "EUR",
"available": true
},
{
"id": "addon_124",
"name": "Equipment Upgrade",
"description": "Premium equipment set",
"addonType": "upgrade",
"mode": "per_participant",
"priceType": "per_person",
"unitPrice": 10.0,
"currency": "EUR",
"available": true
}
]
}

Response Fields

FieldTypeDescription
product_idstringProduct identifier
addonsarrayArray of available add-ons
addons[].idstringAdd-on identifier
addons[].namestringAdd-on name
addons[].descriptionstringAdd-on description
addons[].addonTypestringAdd-on type: item, service, upgrade, or resource
addons[].modestringSelection mode: yesno (checkbox), free_qty (customer enters quantity), or per_participant (matches party size)
addons[].priceTypestringPrice calculation: fixed, per_person, or percentage
addons[].pricenumberFixed price (present when priceType is fixed and mode is yesno)
addons[].unitPricenumberPrice per unit (present when mode is free_qty or per_participant)
addons[].currencystringCurrency code
addons[].availablebooleanWhether add-on is currently available

Mode and pricing details: See the Add-ons section in the Catalog Guide for full descriptions of types, modes, and price types.

Usage Examples

cURL

# Query available slots
curl -X GET "http://localhost:3001/api/v1/availability?product_id=p_demo&site_id=s_demo&date_from=2025-10-10&date_to=2025-10-17&party_size=4&include_labels=true" \
-H "x-tenant-id: t_demo"

# Get product add-ons
curl -X GET "http://localhost:3001/api/v1/availability/products/p_demo/addons" \
-H "x-tenant-id: t_demo"

JavaScript/TypeScript

interface AvailabilityQuery {
productId: string;
siteId: string;
dateFrom: string;
dateTo?: string;
partySize?: number;
includeLabels?: boolean;
}

interface Slot {
slotId: string;
startsAt: string;
endsAt: string;
availableCapacity: number;
totalCapacity: number;
labelCodes: string[];
pricingPreview?: {
subtotal: number;
taxTotal: number;
grandTotal: number;
currency: string;
};
}

async function queryAvailability(
tenantId: string,
query: AvailabilityQuery,
): Promise<Slot[]> {
const params = new URLSearchParams({
product_id: query.productId,
site_id: query.siteId,
date_from: query.dateFrom,
party_size: (query.partySize || 1).toString(),
include_labels: (query.includeLabels !== false).toString(),
});

if (query.dateTo) {
params.append('date_to', query.dateTo);
}

const response = await fetch(
`http://localhost:3001/api/v1/availability?${params}`,
{
headers: {
'x-tenant-id': tenantId,
},
},
);

if (!response.ok) {
throw new Error('Failed to query availability');
}

const data = await response.json();
return data.slots || [];
}

// Usage
const slots = await queryAvailability('t_demo', {
productId: 'p_demo',
siteId: 's_demo',
dateFrom: '2025-10-10',
dateTo: '2025-10-17',
partySize: 4,
includeLabels: true,
});

console.log(`Found ${slots.length} available slots`);

Python

import requests
from typing import Optional, List, Dict

def query_availability(
tenant_id: str,
product_id: str,
site_id: str,
date_from: str,
date_to: Optional[str] = None,
party_size: int = 1,
include_labels: bool = True,
) -> List[Dict]:
params = {
'product_id': product_id,
'site_id': site_id,
'date_from': date_from,
'party_size': party_size,
'include_labels': include_labels,
}

if date_to:
params['date_to'] = date_to

response = requests.get(
'http://localhost:3001/api/v1/availability',
params=params,
headers={'x-tenant-id': tenant_id},
)

response.raise_for_status()
data = response.json()
return data.get('slots', [])

# Usage
slots = query_availability(
tenant_id='t_demo',
product_id='p_demo',
site_id='s_demo',
date_from='2025-10-10',
date_to='2025-10-17',
party_size=4,
include_labels=True,
)

print(f'Found {len(slots)} available slots')

Slot Labels

Slots can have labels that indicate special conditions or requirements:

  • adults_only: Slot is restricted to adults only
  • toddler_friendly: Slot is suitable for toddlers
  • beginner_friendly: Suitable for beginners
  • advanced: For advanced participants only

Labels are included in the labelCodes array when include_labels=true. Always check labels to ensure they match your requirements before creating a booking.

Pricing Preview

The pricingPreview field provides an estimated price for the specified party size. This is a preliminary calculation and may change when you create a booking hold, as:

  • Exact pricing rules are applied
  • Promotions may be available
  • Tax rates may vary

Always use the pricing hash returned when creating a booking hold to ensure price consistency.