> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bookingshake.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Use Cases & Examples

> Real-world examples and integration patterns for the BookingShake API

## Overview

This guide provides practical examples for common use cases when integrating with the BookingShake API.

## Use Case 1: Simple Single Event

Create a basic event with minimal required information.

**Scenario:** A visitor fills out a simple contact form on your website to request an event.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.bookingshake.io/api/events/create' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "bookings": [
        {
          "date": "20-06-2025",
          "start_time": "18:00",
          "pax": "25",
          "event_type": "Birthday Party"
        }
      ],
      "contact": {
        "title": "Ms",
        "first_name": "Sarah",
        "last_name": "Miller",
        "email": "sarah.miller@email.com",
        "phone": "+33612345678"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const createSimpleEvent = async (formData) => {
    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        bookings: [{
          date: formData.date,
          start_time: formData.time,
          pax: formData.guestCount,
          event_type: formData.eventType
        }],
        contact: {
          title: formData.title,
          first_name: formData.firstName,
          last_name: formData.lastName,
          email: formData.email,
          phone: formData.phone
        }
      })
    });

    return await response.json();
  };
  ```
</CodeGroup>

***

## Use Case 2: Group Bookings (Multiple Events)

Create multiple related events in a single request. Perfect for multi-day conferences or workshops.

**Scenario:** A client wants to book a conference room for a 3-day training session.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.bookingshake.io/api/events/create' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "bookings": [
        {
          "date": "10-04-2025",
          "start_time": "09:00",
          "end_time": "17:00",
          "pax": "40",
          "space_id": "space_training_room",
          "event_type": "Training Session - Day 1"
        },
        {
          "date": "11-04-2025",
          "start_time": "09:00",
          "end_time": "17:00",
          "pax": "40",
          "space_id": "space_training_room",
          "event_type": "Training Session - Day 2"
        },
        {
          "date": "12-04-2025",
          "start_time": "09:00",
          "end_time": "17:00",
          "pax": "40",
          "space_id": "space_training_room",
          "event_type": "Training Session - Day 3"
        }
      ],
      "contact": {
        "title": "Mr",
        "first_name": "Michael",
        "last_name": "Brown",
        "email": "michael.brown@company.com",
        "phone": "+33698765432",
        "position": "Training Manager"
      },
      "source_slug": "direct"
    }'
  ```

  ```javascript Node.js theme={null}
  const createMultiDayEvent = async (startDate, numDays, details) => {
    const bookings = [];

    for (let i = 0; i < numDays; i++) {
      const eventDate = new Date(startDate);
      eventDate.setDate(eventDate.getDate() + i);

      bookings.push({
        date: formatDate(eventDate), // DD-MM-YYYY
        start_time: details.startTime,
        end_time: details.endTime,
        pax: details.pax,
        space_id: details.spaceId,
        event_type: `${details.eventType} - Day ${i + 1}`
      });
    }

    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        bookings,
        contact: details.contact,
        source_slug: 'website'
      })
    });

    return await response.json();
  };

  function formatDate(date) {
    const day = String(date.getDate()).padStart(2, '0');
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const year = date.getFullYear();
    return `${day}-${month}-${year}`;
  }
  ```
</CodeGroup>

<Tip>
  All events created in a single request are automatically grouped together in BookingShake, making it easy to manage multi-day bookings.
</Tip>

<Warning>
  A single `/events/create` request can contain at most **20 bookings**. To create more, split them across multiple requests (remember the 10 req/min rate limit on this endpoint).
</Warning>

***

## Use Case 3: Event with Draft Quotation (Products)

Create an event and generate a draft quotation with products/services.

**Scenario:** A corporate client requests a conference package with catering and AV equipment.

<Warning>
  **Important: Pricing Format**

  Product prices and VAT must be provided in specific formats:

  * **Prices (`price_without_tax`)**: In **cents/centimes**, not euros/dollars
    * Example: €1,500.00 = `150000` cents
    * Example: €35.00 = `3500` cents
  * **VAT (`vat`)**: In **basis points** (1/100th of a percent), not percentages
    * Example: 20% VAT = `2000` basis points
    * Example: 10% VAT = `1000` basis points
    * Range: `0` to `10000` (0% to 100%)
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.bookingshake.io/api/events/create' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "bookings": [
        {
          "date": "15-05-2025",
          "start_time": "10:00",
          "end_time": "16:00",
          "pax": "80",
          "space_id": "space_grand_ballroom",
          "event_type": "Corporate Conference",
          "products": [
            {
              "title": "Full Day Conference Package",
              "quantity": 1,
              "price_without_tax": 150000,
              "vat": 2000
            },
            {
              "title": "Lunch Buffet per Person",
              "quantity": 80,
              "price_without_tax": 3500,
              "vat": 1000
            },
            {
              "title": "AV Equipment Package",
              "quantity": 1,
              "price_without_tax": 45000,
              "vat": 2000
            },
            {
              "title": "Coffee Break (Morning & Afternoon)",
              "quantity": 2,
              "price_without_tax": 800,
              "vat": 1000
            }
          ],
          "comments": "Projector needed, dietary requirements to follow"
        }
      ],
      "contact": {
        "title": "Ms",
        "first_name": "Jennifer",
        "last_name": "Davis",
        "email": "j.davis@techcorp.com",
        "phone": "+33687654321",
        "position": "Events Coordinator"
      },
      "company": {
        "name": "TechCorp International",
        "siret": "12345678901234",
        "address_line_1": "50 Innovation Boulevard",
        "postal_code": "75008",
        "city": "Paris",
        "country": "France"
      },
      "source_slug": "direct"
    }'
  ```

  ```javascript Node.js theme={null}
  const createEventWithDraftQuotation = async (eventData) => {
    const payload = {
      bookings: [{
        date: eventData.date,
        start_time: eventData.startTime,
        end_time: eventData.endTime,
        pax: String(eventData.guestCount),
        space_id: eventData.spaceId,
        event_type: eventData.eventType,
        products: eventData.products.map(product => ({
          title: product.name,
          quantity: product.quantity,
          price_without_tax: Math.round(product.pricePerUnit * 100), // Convert to cents
          vat: product.vatRate * 100 // e.g., 20% = 2000
        })),
        comments: eventData.specialRequests
      }],
      contact: eventData.contact,
      company: eventData.company,
      source_slug: 'website'
    };

    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Failed to create event: ${error.message}`);
    }

    return await response.json();
  };
  ```
</CodeGroup>

<Note>
  **Important:** Prices are in **cents** (e.g., €150.00 = 15000) and VAT is multiplied by 100 (e.g., 20% = 2000).
</Note>

***

## Use Case 4: Dynamic Resource Selection

Retrieve available resources before creating an event to populate dropdowns or validate selections.

**Scenario:** Build a booking form that shows available spaces and sources dynamically.

```javascript theme={null}
// Complete workflow: Fetch resources → Display form → Create event

const BookingFormHandler = {
  async initialize() {
    // Fetch available resources
    const [sources, spaces, statuses] = await Promise.all([
      this.fetchSources(),
      this.fetchSpaces(),
      this.fetchStatuses()
    ]);

    // Populate form dropdowns
    this.populateDropdown('source-select', sources);
    this.populateDropdown('space-select', spaces);
    this.populateDropdown('status-select', statuses);
  },

  async fetchSources() {
    const response = await fetch('https://api.bookingshake.io/api/sources', {
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`
      }
    });
    return await response.json();
  },

  async fetchSpaces() {
    const response = await fetch('https://api.bookingshake.io/api/spaces', {
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`
      }
    });
    return await response.json();
  },

  async fetchStatuses() {
    const response = await fetch('https://api.bookingshake.io/api/status', {
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`
      }
    });
    return await response.json();
  },

  populateDropdown(selectId, items) {
    const select = document.getElementById(selectId);
    items.forEach(item => {
      const option = document.createElement('option');
      option.value = item.value;
      option.textContent = item.label;
      select.appendChild(option);
    });
  },

  async submitBooking(formData) {
    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        bookings: [{
          date: formData.date,
          start_time: formData.startTime,
          end_time: formData.endTime,
          pax: formData.pax,
          space_id: formData.spaceId,
          event_type: formData.eventType
        }],
        contact: {
          title: formData.title,
          first_name: formData.firstName,
          last_name: formData.lastName,
          email: formData.email,
          phone: formData.phone
        },
        source_slug: formData.sourceSlug
      })
    });

    return await response.json();
  }
};

// Initialize when page loads
document.addEventListener('DOMContentLoaded', () => {
  BookingFormHandler.initialize();
});
```

***

## Use Case 5: Using Existing Contacts

Link events to existing contacts instead of creating new ones.

**Scenario:** A returning customer makes a new booking.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.bookingshake.io/api/events/create' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "bookings": [
        {
          "date": "25-07-2025",
          "start_time": "19:00",
          "pax": "30",
          "event_type": "Anniversary Dinner"
        }
      ],
      "contact": {
        "contact_id": "contact_xyz789"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  // If you have a returning customer's contact ID
  const createEventForExistingContact = async (contactId, eventData) => {
    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        bookings: [{
          date: eventData.date,
          start_time: eventData.startTime,
          pax: eventData.pax,
          event_type: eventData.eventType
        }],
        contact: {
          contact_id: contactId
        }
      })
    });

    return await response.json();
  };
  ```
</CodeGroup>

<Tip>
  When `contact_id` is provided, other contact fields (first\_name, last\_name, email) are ignored, and the existing contact is used.
</Tip>

<Note>
  **Automatic deduplication:** Even when creating a new contact (without contact\_id), the API automatically checks if a contact with the same email already exists for your venue or venue group. If found, it reuses the existing contact instead of creating a duplicate.
</Note>

***

## Use Case 6: Event with Custom Fields

Create events with custom field values for contacts, companies, and bookings.

**Scenario:** Your venue has configured custom fields to capture additional information like budget range, event theme, and company size. You need to pass these values when creating events.

<Note>
  **Finding your custom fields:** Use the `GET /fields` endpoint to retrieve all available custom fields for your venue. Custom fields have `isCustom: true` and are identified by their `code` property (format: `custom_XXXXXXXXXXX`). The `collection` property indicates where to use the field:

  * `clients` → use in `contact.custom_fields`
  * `accounts` → use in `company.custom_fields`
  * `reservations` → use in each `booking.custom_fields`
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.bookingshake.io/api/events/create' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "bookings": [
        {
          "date": "15-06-2025",
          "start_time": "18:00",
          "end_time": "23:00",
          "pax": "150",
          "event_type": "Gala Dinner",
          "custom_fields": {
            "custom_a1b2c3d4e5": "20000-50000",
            "custom_f6g7h8i9j0": "Black Tie"
          }
        }
      ],
      "contact": {
        "title": "Mme",
        "first_name": "Marie",
        "last_name": "Dupont",
        "email": "marie.dupont@example.com",
        "phone": "+33612345678",
        "custom_fields": {
          "custom_k1l2m3n4o5": "fr",
          "custom_p6q7r8s9t0": "referral"
        }
      },
      "company": {
        "name": "Luxury Events SA",
        "address_line_1": "10 Avenue des Champs-Élysées",
        "postal_code": "75008",
        "city": "Paris",
        "country": "France",
        "custom_fields": {
          "custom_u1v2w3x4y5": "100-500",
          "custom_z6a7b8c9d0": "premium"
        }
      },
      "source_slug": "website"
    }'
  ```

  ```javascript Node.js theme={null}
  const createEventWithCustomFields = async (eventData, customFields) => {
    const response = await fetch('https://api.bookingshake.io/api/events/create', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.BOOKINGSHAKE_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        bookings: [{
          date: eventData.date,
          start_time: eventData.startTime,
          end_time: eventData.endTime,
          pax: eventData.pax,
          event_type: eventData.eventType,
          custom_fields: customFields.booking // e.g., { custom_a1b2c3d4e5: "20000-50000" }
        }],
        contact: {
          title: eventData.contact.title,
          first_name: eventData.contact.firstName,
          last_name: eventData.contact.lastName,
          email: eventData.contact.email,
          phone: eventData.contact.phone,
          custom_fields: customFields.contact // e.g., { custom_k1l2m3n4o5: "fr" }
        },
        company: eventData.company ? {
          name: eventData.company.name,
          address_line_1: eventData.company.address,
          postal_code: eventData.company.postalCode,
          city: eventData.company.city,
          country: eventData.company.country,
          custom_fields: customFields.company // e.g., { custom_u1v2w3x4y5: "100-500" }
        } : undefined,
        source_slug: 'website'
      })
    });

    return await response.json();
  };
  ```
</CodeGroup>

<Warning>
  **Required custom fields:** If a custom field has `isRequiredInBO: true`, you must provide a value for it. The API will return an error like `missing required custom field "Budget Range" for contact` if a required field is missing.
</Warning>

<Tip>
  Custom field values are stored at the root level of each entity (contact, company, or booking) in BookingShake, alongside standard fields. They can be viewed and edited in the back office.
</Tip>

***

## Use Case 7: Complete Integration Workflow

A full example showing error handling, validation, and best practices.

```javascript theme={null}
class BookingShakeAPI {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseURL = 'https://api.bookingshake.io/api';
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const headers = {
      'Authorization': `Bearer ${this.apiKey}`,
      'Content-Type': 'application/json',
      ...options.headers
    };

    try {
      const response = await fetch(url, {
        ...options,
        headers
      });

      const data = await response.json();

      if (!response.ok) {
        throw new Error(`API Error: ${data.message}`);
      }

      return data;
    } catch (error) {
      console.error('BookingShake API request failed:', error);
      throw error;
    }
  }

  // Validate date format (DD-MM-YYYY)
  validateDate(dateString) {
    const regex = /^\d{2}-\d{2}-\d{4}$/;
    if (!regex.test(dateString)) {
      throw new Error('Invalid date format. Use DD-MM-YYYY');
    }
    return true;
  }

  // Validate time format (HH:mm)
  validateTime(timeString) {
    const regex = /^\d{2}:\d{2}$/;
    if (!regex.test(timeString)) {
      throw new Error('Invalid time format. Use HH:mm');
    }
    return true;
  }

  async getSources() {
    return this.request('/sources');
  }

  async getSpaces() {
    return this.request('/spaces');
  }

  async getStatuses() {
    return this.request('/status');
  }

  async getFields() {
    return this.request('/fields');
  }

  async createEvent(bookingData) {
    // Validate input
    bookingData.bookings.forEach(booking => {
      this.validateDate(booking.date);
      this.validateTime(booking.start_time);
      if (booking.end_time) {
        this.validateTime(booking.end_time);
      }
      if (!booking.pax) {
        throw new Error('Number of guests (pax) is required');
      }
    });

    if (!bookingData.contact) {
      throw new Error('Contact information is required');
    }

    if (!bookingData.contact.contact_id) {
      if (!bookingData.contact.first_name || !bookingData.contact.last_name || !bookingData.contact.email) {
        throw new Error('Contact must have first_name, last_name, and email (title is optional)');
      }
    }

    // Make API request
    return this.request('/events/create', {
      method: 'POST',
      body: JSON.stringify(bookingData)
    });
  }
}

// Usage example
const api = new BookingShakeAPI(process.env.BOOKINGSHAKE_API_KEY);

async function processBooking(formData) {
  try {
    // Get available resources
    const spaces = await api.getSpaces();
    console.log('Available spaces:', spaces);

    // Create the event
    const result = await api.createEvent({
      bookings: [{
        date: formData.date,
        start_time: formData.startTime,
        end_time: formData.endTime,
        pax: formData.guestCount,
        space_id: formData.selectedSpace,
        event_type: formData.eventType
      }],
      contact: {
        title: formData.title,
        first_name: formData.firstName,
        last_name: formData.lastName,
        email: formData.email,
        phone: formData.phone
      },
      source_slug: 'website'
    });

    console.log('Event created successfully:', result);
    return result;

  } catch (error) {
    console.error('Booking failed:', error.message);
    throw error;
  }
}
```

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Validate Before Sending" icon="check-circle">
    Always validate date/time formats and required fields before making API calls to provide better user feedback.
  </Card>

  <Card title="Handle Errors Gracefully" icon="shield-check">
    Implement proper error handling and provide clear error messages to users.
  </Card>

  <Card title="Use Environment Variables" icon="lock">
    Store API keys securely in environment variables, never in your source code.
  </Card>

  <Card title="Fetch Resources Dynamically" icon="arrows-rotate">
    Retrieve sources, spaces, and statuses via API instead of hardcoding them.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/api-reference/quickstart">
    Explore detailed API endpoint documentation
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/api-reference/errors">
    Learn about error codes and troubleshooting
  </Card>
</CardGroup>
