SeatSquirrel SDK Reference
Complete API reference for the SeatSquirrel Designer and Picker SDKs
Overview
SeatSquirrel provides two powerful JavaScript SDKs for embedding interactive seat maps on your website:
- Designer SDK - Embed a full-featured layout editor for creating and editing venue layouts
- Picker SDK - Embed a seat selection interface for ticket purchasing and booking
Both SDKs use PostMessage-based communication with domain verification for secure, stateless embedding. Your data stays in your database - no authentication or server calls to SeatSquirrel required.
Getting Started
Installation
Include the SDK in your HTML:
<!-- Designer SDK -->
<script src="https://seatsquirrel.com/embed/seatsquirrel-designer-sdk.js"></script>
<!-- Picker SDK -->
<script src="https://seatsquirrel.com/embed/seatsquirrel-picker-sdk.js"></script>Or download and host yourself for better control and performance.
Domain Verification
Before using either SDK, add your domain to the allowed list:
- Navigate to Organization → Embed Settings
- Click "Add Domain"
- Enter your full domain with protocol (e.g.,
https://example.comorhttp://localhost:3000) - Save the domain
Important: Include the full URL with protocol (https:// or http://).
Designer SDK
Create and edit venue layouts directly in your application.
Quick Start
const designer = new SeatSquirrel.Designer({
container: '#designer-container',
onReady: function() {
// Designer is ready
},
onExport: function(layoutData) {
// Save layout to your database
saveToDatabase(layoutData);
}
});Constructor
new SeatSquirrel.Designer(options)
| Option | Type | Required | Description |
|---|---|---|---|
container | string | HTMLElement | Yes | CSS selector or DOM element |
baseUrl | string | No | SeatSquirrel base URL (default: https://seatsquirrel.com) |
primaryColor | string | No | Custom primary color (hex without #) |
secondaryColor | string | No | Custom secondary color (hex without #) |
onReady | function | No | Called when designer is ready |
onLayoutLoaded | function | No | Called when layout loads |
onExport | function | No | Important: Called when user exports |
onLayoutChanged | function | No | Called when unsaved changes occur |
onError | function | No | Called on errors |
Methods
loadLayout(layoutData)
Load an existing layout for editing.
designer.loadLayout(savedLayout);getLayout()
Trigger export callback to get current layout.
designer.getLayout(); // Triggers onExportsetReadOnly(readOnly)
Enable or disable editing.
designer.setReadOnly(true); // Read-only mode
designer.setReadOnly(false); // Edit modeclearLayout()
Clear current layout and start fresh.
designer.clearLayout();hasChanges()
Check for unsaved changes.
if (designer.hasChanges()) {
alert('You have unsaved changes!');
}getCurrentLayout()
Get last exported layout (not real-time).
const lastExport = designer.getCurrentLayout();isDesignerReady()
Check if designer is ready.
if (designer.isDesignerReady()) {
designer.loadLayout(data);
}destroy()
Cleanup and remove event listeners.
designer.destroy();Callbacks
onReady()
Designer is ready to receive commands.
onReady: function() {
console.log('Designer ready');
designer.loadLayout(layoutData);
}onLayoutLoaded(layoutData)
Layout successfully loaded.
onLayoutLoaded: function(layoutData) {
console.log('Loaded:', layoutData.metadata.name);
}onExport(layoutData)
Most Important: User clicked export or getLayout() called.
onExport: async function(layoutData) {
await saveToDatabase(layoutData);
alert('Layout saved!');
}onLayoutChanged(data)
Unsaved changes status changed.
onLayoutChanged: function(data) {
document.title = data.hasChanges ? '* Editor' : 'Editor';
}onError(error)
An error occurred.
onError: function(error) {
console.error('Error:', error);
alert('An error occurred');
}Complete Example
const designer = new SeatSquirrel.Designer({
container: '#designer-container',
baseUrl: 'https://seatsquirrel.com',
onReady: async function() {
const layout = await fetch('/api/layouts/123').then(r => r.json());
designer.loadLayout(layout);
},
onExport: async function(layoutData) {
await fetch('/api/layouts/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(layoutData)
});
alert('Saved!');
},
onLayoutChanged: function(data) {
document.getElementById('save-btn').disabled = !data.hasChanges;
}
});
// Trigger save
document.getElementById('save-btn').onclick = () => designer.getLayout();Picker SDK
Embed interactive seat selection for ticket purchasing.
Quick Start
const picker = new SeatSquirrel.StatelessPicker({
container: '#picker-container',
onReady: function() {
picker.loadLayout(layoutData);
},
onComplete: function(data) {
// Handle checkout
processPayment(data.selections);
}
});Constructor
new SeatSquirrel.StatelessPicker(options)
| Option | Type | Required | Description |
|---|---|---|---|
container | string | HTMLElement | Yes | CSS selector or DOM element |
baseUrl | string | No | SeatSquirrel base URL (default: https://seatsquirrel.com) |
primaryColor | string | No | Custom primary color (hex without #) |
secondaryColor | string | No | Custom secondary color (hex without #) |
onReady | function | No | Called when picker is ready |
onLayoutLoaded | function | No | Called when layout loads |
onSelectionChange | function | No | Called when selections change |
onComplete | function | No | Important: Called on checkout |
onError | function | No | Called on errors |
Methods
loadLayout(layoutData)
Required: Load layout before users can select seats.
picker.loadLayout(layoutData);setAvailability(availability)
Set seat/area availability by ID.
picker.setAvailability({
seats: { 'seat-uuid-1': true, 'seat-uuid-2': false },
areas: { 'area-uuid-1': 50 }
});setAvailabilityByLabels(availability)
Set availability using row/seat labels.
picker.setAvailabilityByLabels({
seats: [
{ rowLabel: 'A', seatLabel: '1', isAvailable: false },
{ rowLabel: 'A', seatLabel: '2', isAvailable: true }
],
areas: [
{ areaName: 'Standing Room', availableCount: 50 }
]
});setPricing(pricing)
Override layout pricing with custom prices.
picker.setPricing({
categories: [
{ id: 'standard', name: 'Standard', price: 5000, color: '#3b82f6' } // R50.00 in cents
]
});Note: Prices must be in cents (e.g., 5000 = R50.00).
getSelections()
Request current selections (triggers onSelectionChange).
picker.getSelections();clearSelections()
Clear all selected seats/areas.
picker.clearSelections();getCurrentSelections()
Get last known selections without callback.
const selections = picker.getCurrentSelections();getCurrentTotals()
Get last known totals.
const totals = picker.getCurrentTotals();
console.log(`${totals.count} items, R${totals.amount / 100}`);getLayout()
Get loaded layout metadata.
const layout = picker.getLayout();isPickerReady()
Check if picker is ready.
if (picker.isPickerReady()) {
picker.loadLayout(data);
}destroy()
Cleanup and remove event listeners.
picker.destroy();Callbacks
onReady()
Picker is ready to receive commands.
onReady: function() {
picker.loadLayout(layoutData);
}onLayoutLoaded(layoutData)
Layout successfully loaded.
onLayoutLoaded: function(layoutData) {
console.log('Loaded:', layoutData.metadata.name);
}onSelectionChange(data)
User selected or deselected seats/areas.
onSelectionChange: function(data) {
console.log('Selected:', data.selections);
console.log('Total:', data.totals.amount);
updateCartUI(data);
}Selection Data Structure:
{
selections: [
{
type: 'seat',
id: 'seat-uuid-1',
rowLabel: 'A',
seatLabel: '1',
pricingCategoryId: 'standard',
price: 5000 // In cents
}
],
totals: {
count: 1,
amount: 5000 // R50.00 in cents
}
}onComplete(data)
Most Important: User clicked checkout button.
onComplete: async function(data) {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
selections: data.selections,
total: data.totals.amount
})
});
if (response.ok) {
window.location.href = '/checkout/success';
}
}onError(error)
An error occurred.
onError: function(error) {
console.error('Error:', error);
alert('An error occurred');
}Complete Example
const picker = new SeatSquirrel.StatelessPicker({
container: '#picker-container',
baseUrl: 'https://seatsquirrel.com',
onReady: async function() {
// Load layout
const layout = await fetch('/api/layouts/123').then(r => r.json());
picker.loadLayout(layout);
// Set availability
const availability = await fetch('/api/layouts/123/availability').then(r => r.json());
picker.setAvailability(availability);
},
onSelectionChange: function(data) {
document.getElementById('count').textContent = data.totals.count;
document.getElementById('total').textContent = `R${(data.totals.amount / 100).toFixed(2)}`;
},
onComplete: async function(data) {
await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify(data)
});
alert('Checkout successful!');
}
});Data Structures
DesignerOutput
The layout data structure used by both Designer and Picker:
{
type: 'seatSquirrelLayout',
version: '1.0.0',
layout: {
rows: SeatRow[],
areas: Area[],
pricingCategories: PricingCategory[],
sections: Section[],
drawing: {
rectangles: Rectangle[],
circles: Circle[],
lines: Line[],
textElements: TextElement[],
backgroundColor: string
},
metadata: {
name: string,
description?: string,
venueId?: string
}
},
totals: {
totalSeats: number,
totalRows: number,
totalAreas: number,
totalSections: number
}
}PricingCategory
{
id: string,
name: string,
price: number, // In cents (e.g., 5000 = R50.00)
color: string, // Hex color (e.g., '#3b82f6')
customerNotes?: string
}Availability
// By ID
{
seats: {
[seatId: string]: boolean // true = available, false = sold
},
areas: {
[areaId: string]: number // Available count
}
}
// By Labels
{
seats: [
{
rowLabel: string,
seatLabel: string,
isAvailable: boolean
}
],
areas: [
{
areaName: string,
availableCount: number
}
]
}Framework Integration
React
import { useEffect, useRef } from 'react';
export function DesignerEmbed({ onSave }) {
const containerRef = useRef(null);
const designerRef = useRef(null);
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://seatsquirrel.com/embed/seatsquirrel-designer-sdk.js';
script.async = true;
script.onload = () => {
designerRef.current = new window.SeatSquirrel.Designer({
container: containerRef.current,
onExport: (layoutData) => {
onSave(layoutData);
}
});
};
document.body.appendChild(script);
return () => {
designerRef.current?.destroy();
document.body.removeChild(script);
};
}, [onSave]);
return <div ref={containerRef} style={{ width: '100%', height: '600px' }} />;
}Vue
<template>
<div ref="container" style="width: 100%; height: 600px;"></div>
</template>
<script>
export default {
props: {
onSave: Function
},
data() {
return {
designer: null
};
},
mounted() {
this.loadSDK();
},
beforeUnmount() {
this.designer?.destroy();
},
methods: {
async loadSDK() {
await new Promise((resolve) => {
const script = document.createElement('script');
script.src = 'https://seatsquirrel.com/embed/seatsquirrel-designer-sdk.js';
script.onload = resolve;
document.body.appendChild(script);
});
this.designer = new window.SeatSquirrel.Designer({
container: this.$refs.container,
onExport: (layoutData) => {
this.onSave(layoutData);
}
});
}
}
};
</script>Best Practices
1. Container Requirements
Both SDKs require explicit container dimensions:
<!-- ✅ Good: Explicit height -->
<div id="container" style="width: 100%; height: 600px;"></div>
<!-- ❌ Bad: No height -->
<div id="container" style="width: 100%;"></div>Recommended heights:
- Minimum: 500px
- Designer: 700-800px
- Picker: 600px
- Full-screen: Best experience
2. Wait for onReady
Always wait for onReady before calling methods:
const instance = new SeatSquirrel.Designer({
container: '#container',
onReady: function() {
// Safe to call methods now
instance.loadLayout(data);
}
});
// ❌ Don't call methods immediately
instance.loadLayout(data);3. Error Handling
Always implement error handling:
onError: function(error) {
console.error('SDK error:', error);
// Show user-friendly message
showNotification('An error occurred. Please try again.');
// Log to error tracking
logToErrorService(error);
}4. Price Format
Always use cents for prices:
// ✅ Correct: Prices in cents
{ id: 'standard', name: 'Standard', price: 5000 } // R50.00
// ❌ Incorrect: Prices in rands
{ id: 'standard', name: 'Standard', price: 50 }5. Cleanup on Unmount
Always cleanup when component unmounts:
// React
useEffect(() => {
// ... setup ...
return () => {
instance.destroy();
};
}, []);
// Vue
beforeUnmount() {
this.instance?.destroy();
}Troubleshooting
"Access Denied" Error
Solutions:
- Verify domain added with protocol:
https://example.com - Include port for localhost:
http://localhost:3000 - Check browser console for details
SDK Not Loading
Solutions:
- Verify SDK script loaded successfully
- Check network tab for 404 errors
- Ensure container element exists
- Check JavaScript console for errors
Methods Not Working
Solutions:
- Wait for
onReadybefore calling methods - Check
isDesignerReady()orisPickerReady() - Verify correct method names and parameters
- Check browser console for errors
Additional Resources
- Designer Embedding Guide - Complete Designer SDK documentation
- Picker Embedding Guide - Complete Picker SDK documentation
- Organization Settings - Manage allowed domains
- Quick Start Guide - Get started with SeatSquirrel
Support
Need help? Contact us at support@seatsquirrel.com