Quick Start
Basic Implementation
<!-- Include the widget CSS and JavaScript -->
<link rel="stylesheet" href="https://widget.bookabletech.com/style.css">
<script src="https://widget.bookabletech.com/bookings.js"
data-bookable-widget
data-venue-id="51e00dde0df690ee62001cea">
</script>
With Configuration
<script src="https://widget.bookabletech.com/bookings.js"
data-bookable-widget
data-venue-id="51e00dde0df690ee62001cea"
data-guests="4"
data-date="2026-03-21"
data-booking-type="5c4af02d6354a83e3a0ea3b4"
data-alternatives-limit="5">
</script>
Installation
Via CDN (Recommended)
<link rel="stylesheet" href="https://widget.bookabletech.com/style.css">
<script src="https://widget.bookabletech.com/bookings.js"
data-bookable-widget
data-venue-id="YOUR_VENUE_ID">
</script>
Self-Hosted
- Download
bookings.jsandstyle.css - Host them on your server
- Include them in your HTML
<link rel="stylesheet" href="/path/to/style.css">
<script src="/path/to/bookings.js"
data-bookable-widget
data-venue-id="YOUR_VENUE_ID">
</script>
Configuration
Required Attributes
| Attribute | Type | Description |
|---|---|---|
data-bookable-widget |
- | Required flag to initialize the widget |
data-venue-id |
string | Collins venue ID or "all" for multi-venue selection |
Optional Attributes
Pre-fill Data
| Attribute | Type | Default | Description |
|---|---|---|---|
data-guests |
number | 2 |
Default number of guests |
data-date |
string | - | Default date (YYYY-MM-DD format) |
data-booking-type |
string | - | Default booking type ID |
- If all three are provided: Widget skips directly to time selection
- If
data-dateanddata-booking-type(nodata-guests): Shows condensed view with guest selector - Otherwise: Normal step-by-step flow
Analytics
| Attribute | Type | Description |
|---|---|---|
data-ga4-id |
string | Google Analytics 4 Measurement ID (e.g., G-XXXXXXXXXX) |
data-gtm-id |
string | Google Tag Manager Container ID (e.g., GTM-XXXXXX) |
Tracking
| Attribute | Type | Description |
|---|---|---|
data-custom-source |
string | Custom source identifier for tracking bookings |
data-affiliate-id |
string | Affiliate ID for commission tracking |
Widget Options
| Attribute | Type | Default | Description |
|---|---|---|---|
data-hide-offers |
boolean | false |
Hide special offers display |
data-hide-cross-sell |
boolean | false |
Hide cross-sell promotions |
data-monday-first |
boolean | true |
Calendar starts on Monday (false for Sunday) |
Alternatives Settings
| Attribute | Type | Default | Description |
|---|---|---|---|
data-alternatives-limit |
number | 5 |
Number of closest alternative venues to show |
data-alternatives-radius |
number | - | Show venues within X miles (overrides limit if set) |
- If
data-alternatives-radiusis set: Shows all venues within that radius (in miles) - Otherwise: Shows the closest X venues based on
data-alternatives-limit
Return URL Settings
| Attribute | Type | Default | Description |
|---|---|---|---|
data-return-url |
string | - | URL to redirect to after booking completion on DesignMyNight |
data-return-method |
string | post |
HTTP method for return redirect (post or get) |
- When set, users are redirected to this URL after completing their booking on DesignMyNight
- The
return_methoddetermines how data is sent:postsends booking data in the request body,getappends it as query parameters
Complete Example
<script src="https://widget.bookabletech.com/bookings.js"
data-bookable-widget
data-venue-id="51e00dde0df690ee62001cea"
data-guests="4"
data-date="2026-03-21"
data-booking-type="5c4af02d6354a83e3a0ea3b4"
data-ga4-id="G-XXXXXXXXXX"
data-gtm-id="GTM-XXXXXX"
data-custom-source="email-campaign"
data-affiliate-id="partner-123"
data-monday-first="true"
data-alternatives-limit="5"
data-alternatives-radius="2">
</script>
JavaScript API
Control the widget programmatically using the JavaScript API.
Getting a Widget Instance
// Get the first widget on the page
var widget = BookableWidget.getWidget();
// Get widget by index (0-based)
var widget = BookableWidget.getWidget(0);
// Get widget by container element
var container = document.querySelector('.bookable-widget-root');
var widget = BookableWidget.getWidget(container);
// Get all widgets on the page
var allWidgets = BookableWidget.getAllWidgets();
API Methods
setVenue(venueId)
Set the venue for the widget.
widget.setVenue('589478b336288d992b46ffdd');
Parameters:
venueId(string) - Collins venue ID
Behavior:
- Fetches venue details
- Resets to Stage 1 (guest and date selection)
setDate(date)
Set the booking date.
// Using date string (YYYY-MM-DD)
widget.setDate('2026-03-21');
// Using Date object
widget.setDate(new Date('2026-03-21'));
Parameters:
date(string | Date) - Date in YYYY-MM-DD format or Date object
Behavior:
- If venue and guests are set, automatically fetches available booking types
setGuests(count)
Set the number of guests.
widget.setGuests(10);
Parameters:
count(number) - Number of guests (1-50)
Behavior:
- If venue and date are set, automatically fetches available booking types
setBookingType(bookingTypeId)
Set the booking type and advance to time selection.
widget.setBookingType('5c4af02d6354a83e3a0ea3b4');
Parameters:
bookingTypeId(string) - Booking type ID
Behavior:
- Must be called after venue, date, and guests are set
- Automatically fetches availability and advances to Stage 3 (time selection)
setNotes(notes)
Set special requests or notes for the booking.
widget.setNotes('Please seat us by the window');
Parameters:
notes(string) - Special request or notes text
Behavior:
- Notes are included in the booking URL when user completes the booking
- Persists until widget is reset or
setNotes('')is called - Applies to all booking types (fixed duration, variable duration, and alternatives)
reset()
Reset the widget to its initial state.
widget.reset();
Behavior:
- Clears all selections
- Resets to Stage 0 (venue selection) or Stage 1 (if venue was pre-configured)
- Restores default guest count from initial configuration
getState()
Get the current widget state.
var state = widget.getState();
console.log(state);
Returns:
{
venue: {...}, // Selected venue object
date: Date, // Selected date
guests: number, // Number of guests
bookingType: {...}, // Selected booking type object
stage: number, // Current stage (0-3)
availableBookingTypes: [] // Available booking types array
}
getContainer()
Get the widget's container DOM element.
var container = widget.getContainer();
container.style.border = '2px solid red';
Returns:
HTMLElement- The widget container element
Complete API Example
// Wait for widget to load
window.addEventListener('DOMContentLoaded', function() {
// Get widget instance
var widget = BookableWidget.getWidget();
// Configure the widget
widget.setVenue('589478b336288d992b46ffdd');
widget.setGuests(10);
widget.setDate('2026-03-21');
widget.setBookingType('5c4af02d6354a83e3a0ea3b4');
// Check state
setTimeout(() => {
var state = widget.getState();
console.log('Widget is on stage:', state.stage);
}, 1000);
});
Features
1. Venue Selection
- Single venue mode (fixed venue)
- Multi-venue mode (search and select)
- Venue details with offers
2. Guest and Date Selection
- Guest count selector (1-50 guests)
- Calendar with today/tomorrow quick buttons
- Monday or Sunday week start
- Disabled past dates
3. Booking Type Selection
- Available and unavailable booking types
- Special offers display
- Invalid type messaging
- Auto-advance when pre-selected
4. Time Selection
- Fixed Duration: Single column time selection
- Variable Duration: Two-column flow (start time → end time)
- Action types:
- Instant Book (accept) - Lightning icon
- Enquire (may_enquire, can_accept) - Clock icon
- Loading states
5. Alternative Venues
When a selected time is unavailable, users can:
- Click "Find alternatives" button
- View carousel of nearby venues
- See available times within ±30 minutes
- Navigate between venues with Previous/Next
- Book directly at alternative venue
Features:
- Distance displayed in miles
- Multiple products per venue support
- Product names shown when venue has multiple options
- Direct booking from alternative
6. Condensed Mode
Activated when data-date and data-booking-type are provided without data-guests:
<script src="bookings.js"
data-bookable-widget
data-venue-id="51e00dde0df690ee62001cea"
data-date="2026-03-21"
data-booking-type="5c4af02d6354a83e3a0ea3b4">
</script>
Display:
- Venue name header
- Date with EDIT button
- Booking type with EDIT button
- Guest selector dropdown
- CONTINUE button
User Flow:
- Confirm or change guest count
- Click CONTINUE
- Advances to time selection
Action Types
| Action Type | Icon | Behavior |
|---|---|---|
accept |
⚡ Lightning | Instant booking available |
can_accept |
🕐 Clock | Enquiry (can be accepted) |
may_enquire |
🕐 Clock | Enquiry only |
reject |
- | Not available (disabled) |
API Endpoints
The widget backend provides several REST endpoints:
GET /api/health
Health check endpoint.
Response:
{
"status": "ok",
"timestamp": "2026-03-12T10:00:00.000Z"
}
GET /api/venues
Get all available venues.
Response:
{
"venues": [
{
"id": "589478b336288d992b46ffdd",
"name": "The Director's Box, Manchester",
"address": "123 Street Name, Manchester"
}
]
}
GET /api/venues/:venueId
Get specific venue details with offers.
Parameters:
venueId- Venue Collins ID
Response:
{
"id": "589478b336288d992b46ffdd",
"name": "The Director's Box, Manchester",
"address": "123 Street Name, Manchester",
"offers": [
{
"_id": "offer123",
"title": "Early Bird Special",
"description": "20% off before 6pm"
}
]
}
GET /api/types
Get available booking types for a venue on a specific date.
Query Parameters:
venueId(required) - Venue Collins IDguests(required) - Number of guestsdate(required) - Date in YYYY-MM-DD format
Response:
{
"availableTypes": [
{
"id": "5c4af02d6354a83e3a0ea3b4",
"name": "Lunch",
"valid": true,
"offers": ["offer123"]
}
],
"unavailableTypes": [
{
"id": "5c5ac86d6a2c3d0af2068a49",
"name": "Dinner",
"valid": false,
"message": "Not available on this date"
}
]
}
GET /api/availability
Get availability, rules, and times for a booking type.
Query Parameters:
venueId(required) - Venue Collins IDbookingTypeId(required) - Booking type IDguests(required) - Number of guestsdate(required) - Date in YYYY-MM-DD formattime(optional) - Specific time for duration lookup (HH:MM format)fields(optional) - When set to "duration", returns available durations
Response (Standard):
{
"rules": {
"min_duration": 90,
"max_duration": 180,
"no_duration": false
},
"disabledDates": ["2026-03-25", "2026-03-26"],
"times": [
{
"time": "12:00",
"action": "accept",
"available": true
}
]
}
Response (Duration Mode - when fields=duration):
{
"durations": [
{
"value": 90,
"action": "accept"
},
{
"value": 120,
"action": "accept"
}
]
}
GET /api/alternatives
Get alternative venues when requested time is unavailable.
Query Parameters:
venueId(required) - Original venue Collins IDbookingTypeId(required) - Booking type IDguests(required) - Number of guestsdate(required) - Date in YYYY-MM-DD formattime(required) - Requested time (HH:MM format)limit(optional) - Number of closest venues (default: 5)radius(optional) - Radius in miles (overrides limit)
Response:
{
"venueId": "589478b336288d992b46ffdd",
"bookingTypeId": "5c4af02d6354a83e3a0ea3b4",
"guests": 10,
"date": "2026-03-21",
"requestedTime": "14:00",
"alternatives": [
{
"id": "512f77e80df690715c0000a3",
"name": "Slug and Lettuce Albert Square",
"distance": "0.17",
"products": [
{
"id": "5c4af02d6354a83e3a0ea3b4",
"name": "Lunch",
"valid": true,
"times": [
{
"time": "13:30",
"duration": 90
}
]
}
]
}
]
}
Algorithm:
- Finds venues with matching product category
- Uses PostGIS for distance calculation
- Queries DesignMyNight API for availability
- Filters to ±30 minute time window
- Returns venues sorted by distance (closest first)