← Documentation

Booking and available-slot integration

New to the package? Start with the JavaScript scheduling calendar overview.

Premium, headless, and backend-neutral. The availability calculator and booking scheduler provide policy and workflow APIs. Your application owns the form UI, HTTP endpoints, authentication, and durable storage.

Keep rendering and availability queries bounded

The rendered calendar should load only its visible date range. A booking form may search a different bounded range and pass those busy events to calculateAvailableSlots(). The calculator intentionally performs no HTTP request: endpoint paths, authorization, headers, tenant rules, and response mapping belong to the consuming application.

Do not download a full year of high-volume bookings just to render one month. Request the visible or searched range and resource, cancel obsolete requests, and optionally cache adjacent ranges. A month-wise event source and a slot-search request are related but independent data flows.

Fetch busy events, then calculate slots

Server-backed available slots
import {
  calculateAvailableSlots,
} from '@wts-calendar/core/availability-scheduling';

async function findSlots(rangeStart, rangeEnd, resource, signal) {
  const query = new URLSearchParams({
    start: rangeStart.toISOString(),
    end: rangeEnd.toISOString(),
    resourceId: resource.id,
  });

  const response = await fetch('/api/bookings?' + query, { signal });
  if (!response.ok) throw new Error('Unable to load bookings');
  const events = await response.json();

  return calculateAvailableSlots({
    license,
    start: rangeStart,
    end: rangeEnd,
    resource,
    events,
    slotDuration: {
      defaultMinutes: 30,
      allowedMinutes: [15, 30, 60],
    },
    minimumNoticeMinutes: 120,
    eventBufferBeforeMinutes: 10,
    eventBufferAfterMinutes: 15,
    timeZone: 'Europe/London',
    customerTimeZone: 'Asia/Kolkata',
  });
}

Slot results include calendar-zone ISO values, optional customer-zone values, the selected duration, resource ID, and remaining capacity. Existing event times are not changed when buffers are applied.

  • Configurable default, allowed, minimum, maximum, and increment durations
  • Minimum booking notice with a deterministic clock override
  • Buffers before and after existing appointments
  • Global or resource working hours and exact unavailable ranges
  • Recurring busy events, time-zone conversion, resource capacity, and group units

Coordinate a custom booking interface

CalendarBookingScheduler is UI-independent. Connect it to any form, modal, page, framework component, REST API, GraphQL service, or local prototype. It returns immutable snapshots and structured validation issues without requiring the rendered calendar.

Headless scheduler setup
import {
  CalendarBookingScheduler,
} from '@wts-calendar/core/availability-scheduling';

const bookings = new CalendarBookingScheduler({
  license,
  origin: window.location.origin,
  timeZone: 'Europe/London',
  businessHours: true,
  unavailable: companyClosures,
  slotDuration: { defaultMinutes: 30, allowedMinutes: [15, 30, 60] },
  minimumNoticeMinutes: 60,
  eventBufferBeforeMinutes: 10,
  eventBufferAfterMinutes: 10,
  resources: staff,
  events: existingCalendarEvents,
  appointments: restoredAppointments,
  roundRobinCursor: restoredCursor,
  persistenceAdapter: {
    load: (signal) => api.loadBookingState({ signal }),
    commit: (context) => api.commitBooking(context),
  },
  hooks: {
    transform: (request) => sanitizeBookingForm(request),
    validate: ({ request }) => validateBookingForm(request),
    beforeAction: ({ action, appointment }) => authorize(action, appointment),
    afterAction: ({ action, appointment }) => notify(action, appointment),
    onError: ({ action, error }) => reportBookingFailure(action, error),
  },
});

Manual assignment validates a chosen resource. Round-robin assignment scans an eligible staff pool deterministically and advances its cursor only after a successful commit. Weighted requestedUnits support capacity-based group bookings.

Confirmation, approval, rescheduling, and cancellation

Appointment lifecycle
const pending = await bookings.create({
  title: 'Product consultation',
  start: '2026-10-01T09:00:00',
  durationMinutes: 30,
  customerTimeZone: 'Asia/Kolkata',
  assignment: {
    mode: 'round-robin',
    resourceIds: ['sam', 'lee'],
  },
  requestedUnits: 1,
  requiresApproval: true,
  formValues: { email: 'buyer@example.com' },
});

await bookings.confirm(pending.id);
await bookings.approve(pending.id);
await bookings.reschedule(pending.id, {
  start: '2026-10-01T10:00:00',
  durationMinutes: 30,
});
await bookings.cancel(pending.id, 'Customer requested cancellation');

Appointment states are pending, confirmed, approved, rejected, and cancelled. Rejected and cancelled records remain available for history but stop reserving capacity. Form transformation, validation, before-action, after-action, status-specific, and error hooks let the host connect policy, analytics, notifications, and custom UI feedback.

Make the backend authoritative

One scheduler instance serializes its mutations and rechecks availability immediately before a local commit. The optional persistence adapter commits before local state changes and supports idempotency keys, optimistic revisions, abort signals, and structured conflict or rejection results.

Separate browsers are not made atomic by client-side calculation. The backend must repeat the time, resource, and capacity check inside a transaction or equivalent atomic write. A rejected server commit leaves the scheduler's previous local state intact.

Compatibility

This API is additive. Existing calendar rendering, event sources, resource views, and calculateAvailableSlots() integrations keep their behavior. Applications opt in through the Premium @wts-calendar/core/availability-scheduling entry.