Skip to content

@thebookingkit/core API

All exports from @thebookingkit/core.

import { getAvailableSlots, isSlotAvailable } from "@thebookingkit/core";
// Compute available slots for a date range
const slots = getAvailableSlots(
rules,
overrides,
existingBookings,
{ start: new Date("2026-03-09"), end: new Date("2026-03-13") },
"America/New_York",
{ duration: 30, bufferAfter: 15 }
);
// Check if a single slot is available
const available = isSlotAvailable(
rules,
overrides,
existingBookings,
new Date("2026-03-10T14:00:00.000Z"),
new Date("2026-03-10T14:30:00.000Z")
);
FunctionDescription
getAvailableSlots(rules, overrides, bookings, dateRange, timezone, options?)Compute available time slots
isSlotAvailable(rules, overrides, bookings, start, end, bufferBefore?, bufferAfter?)Check if a specific slot is open
import { parseRecurrence, InvalidRRuleError } from "@thebookingkit/core";
const occurrences = parseRecurrence(
"RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR",
{ start: new Date("2026-03-01"), end: new Date("2026-03-31") },
"09:00",
"17:00"
);
FunctionDescription
parseRecurrence(rrule, dateRange, startTime, endTime)Expand RRULE into date occurrences
import { normalizeToUTC, utcToLocal, isValidTimezone, getTimezoneOffset } from "@thebookingkit/core";
const utc = normalizeToUTC("2026-03-10T09:00:00", "America/New_York");
const local = utcToLocal(new Date("2026-03-10T14:00:00.000Z"), "America/New_York");
const valid = isValidTimezone("America/New_York");
const offset = getTimezoneOffset("America/New_York", new Date());
FunctionDescription
normalizeToUTC(localTime, timezone)Convert local time to UTC Date
utcToLocal(utcDate, timezone)Convert UTC Date to local time string
isValidTimezone(timezone)Validate IANA timezone string
getTimezoneOffset(timezone, date)Get UTC offset in minutes
import { computeBookingLimits, filterSlotsByLimits } from "@thebookingkit/core";
FunctionDescription
computeBookingLimits(config, existingBookings, dateRange)Calculate remaining capacity
filterSlotsByLimits(slots, config, existingBookings)Remove slots that exceed limits
import { validateEventType, generateSlug, validateQuestionResponses, EventTypeValidationError } from "@thebookingkit/core";
import { getInitialBookingStatus, getAutoRejectDeadline, isPendingBookingOverdue, CONFIRMATION_TIMEOUT_HOURS } from "@thebookingkit/core";
import { withAuth, assertProviderOwnership, assertCustomerAccess } from "@thebookingkit/server";
FunctionDescription
withAuth(handler, adapter, options?)Wrap a handler with authentication
assertProviderOwnership(session, providerId)Assert the user owns the provider
assertCustomerAccess(session, customerEmail)Assert the user has access to the customer’s data
import { withSerializableRetry, SerializationRetryExhaustedError } from "@thebookingkit/server";
import { generateBookingToken, verifyBookingToken } from "@thebookingkit/server";
import { getTeamSlots, assignHost, resolveManagedEventType, isFieldLocked, propagateTemplateChanges } from "@thebookingkit/core";
// Compute team slots
const slots = getTeamSlots(teamMembers, dateRange, timezone, options);
// Assign host for a booking
const host = assignHost(teamMembers, bookingCounts, "round_robin");
// Check field locks in managed templates
const locked = isFieldLocked(template, "durationMinutes");
FunctionDescription
getTeamSlots(members, dateRange, timezone, options)Compute available slots across team
assignHost(members, counts, strategy)Assign team member to booking
resolveManagedEventType(template, overrides)Resolve template for member
isFieldLocked(template, field)Check if field is locked
propagateTemplateChanges(template, eventTypes)Propagate template updates
import { evaluateCancellationFee, validateCancellationPolicy, computePaymentSummary, requiresPayment, formatPaymentAmount } from "@thebookingkit/core";
const policy = [
{ hoursBefore: 48, feePercentage: 0 },
{ hoursBefore: 0, feePercentage: 50 },
];
const fee = evaluateCancellationFee(policy, 5000, appointmentStart, cancellationTime);
const summary = computePaymentSummary(paymentRecords);
const formatted = formatPaymentAmount(5000, "usd"); // "$50.00"
FunctionDescription
evaluateCancellationFee(policy, amountCents, start, cancelTime)Calculate cancellation fee
validateCancellationPolicy(policy)Validate policy structure
computePaymentSummary(payments)Aggregate payment data
requiresPayment(eventType)Check if payment required
formatPaymentAmount(cents, currency)Format amount for display
import { signWebhookPayload, verifyWebhookSignature, matchWebhookSubscriptions } from "@thebookingkit/server";
const signature = signWebhookPayload(JSON.stringify(payload), secret, timestamp);
const result = verifyWebhookSignature(body, signature, timestamp, secret);
const matching = matchWebhookSubscriptions(subscriptions, "booking.created");
FunctionDescription
signWebhookPayload(payload, secret, timestamp)Sign webhook with HMAC
verifyWebhookSignature(payload, sig, ts, secret, tolerance?)Verify webhook signature
matchWebhookSubscriptions(subscriptions, trigger)Find subscriptions for trigger
getRetryDelay(attempt, config)Calculate retry delay
import { generateOccurrences, checkRecurringAvailability, cancelFutureOccurrences } from "@thebookingkit/core";
const occurrences = generateOccurrences({
startsAt: new Date(),
durationMinutes: 30,
frequency: "weekly",
count: 8,
});
const availability = checkRecurringAvailability(occurrences, rules, overrides, bookings);
const cancelled = cancelFutureOccurrences(seriesBookings, new Date("2026-04-01"));
FunctionDescription
generateOccurrences(input)Generate series occurrences
checkRecurringAvailability(occurrences, rules, overrides, bookings)Validate availability
cancelFutureOccurrences(bookings, fromDate)Cancel future bookings
isValidFrequency(frequency)Validate frequency string
import { computeSeatAvailability, canReserveSeat, isGroupEvent, computeGroupEventSummary, formatSeatCount, validateSeatReservation, SeatError } from "@thebookingkit/core";
import { sendConfirmationEmail, sendReminderEmail, sendCancellationEmail, sendRescheduleEmail, scheduleAutoReject, syncBookingToCalendar, deleteBookingFromCalendar, formatDateTimeForEmail, formatDurationForEmail } from "@thebookingkit/server";
import { interpolateTemplate, CONFIRMATION_EMAIL_HTML, CONFIRMATION_EMAIL_TEXT, REMINDER_EMAIL_HTML, CANCELLATION_EMAIL_HTML, RESCHEDULE_EMAIL_HTML } from "@thebookingkit/server";
import { validateRoutingForm, evaluateRoutingRules, validateRoutingResponses, computeRoutingAnalytics, RoutingFormValidationError } from "@thebookingkit/core";
import { validateEmbedConfig, generateEmbedSnippet, generateAllSnippets, buildEmbedUrl, EmbedConfigError } from "@thebookingkit/core";
import { createErrorResponse, createSuccessResponse, createPaginatedResponse, generateApiKey, hashApiKey, verifyApiKey, hasScope, isKeyExpired, checkRateLimit, encodeCursor, decodeCursor, validateSlotQueryParams, parseSortParam, API_ERROR_CODES } from "@thebookingkit/server";
import { COMPONENT_REGISTRY, findComponent, resolveComponentDependencies, listComponents, createManifestEntry, hasLocalModifications, generateThebookingkitConfig, generateEnvTemplate, parseMigrationFiles, getPendingMigrations, DEFAULT_MANIFEST } from "@thebookingkit/cli";
import { resolveEffectiveSettings, getRolePermissions, roleHasPermission, assertOrgPermission, assertTenantScope, buildOrgBookingUrl, parseOrgBookingPath, TenantAuthorizationError, GLOBAL_DEFAULTS } from "@thebookingkit/server";
import { BookingConflictError, SerializationRetryExhaustedError, UnauthorizedError, ForbiddenError } from "@thebookingkit/core";
import type { AuthAdapter, EmailAdapter, CalendarAdapter, JobAdapter, PaymentAdapter, StorageAdapter, SmsAdapter } from "@thebookingkit/server";
import { generateICSAttachment, JOB_NAMES } from "@thebookingkit/server";

Imported from @thebookingkit/core. Enables providers to accept walk-in customers alongside scheduled appointments. Walk-ins are placed in gaps between existing bookings, with automatic wait-time estimation and a lifecycle state machine.

TypeDescription
BookingSource"online" | "walk_in" | "phone" | "admin" — how a booking was created
WalkInStatus"queued" | "in_service" | "completed" | "no_show" | "cancelled"
WalkInQueueEntryFull walk-in record: id, bookingId, providerId, queuePosition, estimatedWaitMinutes, checkedInAt, serviceStartedAt, completedAt, status, customerName, durationMinutes, and optional contact fields
AddWalkInInputInput shape for adding a walk-in: providerId, eventTypeId, durationMinutes, customerName, optional contact, optional buffers
AddWalkInResultResult of adding a walk-in: queueEntry, estimatedStartTime, estimatedWaitMinutes, queuePosition
WaitTimeEstimateestimatedMinutes, queueLength, nextAvailableAt
ProviderWalkInStateacceptingWalkIns: boolean, withinWorkingHours: boolean
WalkInAnalyticsAggregated analytics — see computeWalkInAnalytics
import {
estimateWaitTime,
findNextAvailableGap,
isValidQueueTransition,
validateQueueTransition,
recomputeQueuePositions,
reorderQueue,
recomputeWaitTimes,
isAcceptingWalkIns,
computeWalkInAnalytics,
} from "@thebookingkit/core";
// Estimate how long a new walk-in will wait
const estimate = estimateWaitTime(queue, existingBookings, 30);
// => { estimatedMinutes: 45, queueLength: 2, nextAvailableAt: Date }
// Find the earliest start time for a new walk-in
const startTime = findNextAvailableGap(queue, existingBookings, 30, 5, 5);
// State machine guard
const ok = isValidQueueTransition("queued", "in_service"); // true
const bad = isValidQueueTransition("completed", "queued"); // false
// Throws InvalidQueueTransitionError if invalid
validateQueueTransition("queued", "in_service");
// Normalise positions after a removal
const updated = recomputeQueuePositions(activeEntries);
// Drag-and-drop reorder
const reordered = reorderQueue(entries, ["id-3", "id-1", "id-2"]);
// Refresh all estimated wait times after a change
const refreshed = recomputeWaitTimes(queue, existingBookings);
// Check provider walk-in status right now
const state = isAcceptingWalkIns(true, rules, overrides);
// => { acceptingWalkIns: true, withinWorkingHours: true }
// Aggregate analytics for a reporting period
const analytics = computeWalkInAnalytics(entriesInRange, totalBookings);
FunctionSignatureDescription
estimateWaitTime(queue, existingBookings, serviceDuration, now?)Estimate wait minutes for a new walk-in based on active queue and any in-progress appointment
findNextAvailableGap(queue, existingBookings, durationMinutes, bufferBefore?, bufferAfter?, now?)Find the earliest moment the provider can begin serving a new walk-in
isValidQueueTransition(from, to)Return true if the WalkInStatus transition is permitted by the state machine
validateQueueTransition(current, target)Assert the transition is valid; throws InvalidQueueTransitionError otherwise
recomputeQueuePositions(entries)Renumber queuePosition fields starting from 1, preserving input order
reorderQueue(entries, orderedIds)Apply a new position order by entry ID; keeps the in-service entry at position 1
recomputeWaitTimes(queue, existingBookings, now?)Refresh estimatedWaitMinutes on every queued entry after any queue change
isAcceptingWalkIns(acceptingWalkIns, rules, overrides, now?)Check the provider’s toggle and current availability window
computeWalkInAnalytics(entries, totalBookings)Return aggregated WalkInAnalytics including no-show rate, hourly/daily distributions, and walk-in ratio

Valid transitions for WalkInStatus:

FromAllowed targets
queuedin_service, no_show, cancelled
in_servicecompleted, no_show
completed— (terminal)
no_show— (terminal)
cancelled— (terminal)
ClasscodeThrown when
WalkInsDisabledErrorWALK_INS_DISABLEDProvider’s acceptingWalkIns flag is false
QueueEntryNotFoundErrorQUEUE_ENTRY_NOT_FOUNDReferenced queue entry ID does not exist
InvalidQueueTransitionErrorINVALID_QUEUE_TRANSITIONvalidateQueueTransition is called with a disallowed from → to pair

Imported from @thebookingkit/core. Configuration, validation, and utilities for the interactive kiosk calendar view (E-20). Handles display settings, reschedule validation, break/block management, conflict detection, and multi-provider resource views.

TypeDescription
BlockDensityMode"compact" | "standard" | "detailed" — calendar block density
ColorCodingMode"status" | "event_type" | "source" — block color scheme
KioskViewType"day" | "3day" | "week" — default calendar view
KioskFieldVisibilityObject controlling which fields appear on compact blocks and detail popovers
KioskSettingsFull kiosk configuration: view, density, color coding, field visibility, auto-lock, PIN, walk-in sidebar, slot height, day start/end hours
RescheduleValidation{ valid, reason?, conflictDetails? } — result of validateReschedule
BlockType"break" | "personal" | "meeting" | "closed"
BreakBlockInput{ title, startTime, endTime, blockType, recurring }
KioskProvider{ id, displayName, acceptingWalkIns, queueCount, visible }
import { DEFAULT_KIOSK_SETTINGS } from "@thebookingkit/core";
// Full default KioskSettings object:
// {
// defaultView: "day",
// blockDensity: "standard",
// colorCoding: "status",
// autoLockMinutes: 5,
// pinHash: "",
// showWalkInSidebar: true,
// slotHeightPx: 48,
// dayStartHour: 6,
// dayEndHour: 22,
// compactFields: { customerName: true, serviceName: true, ... },
// detailFields: { customerName: true, customerEmail: true, ... },
// }
import {
validateKioskSettings,
resolveKioskSettings,
findConflicts,
canReschedule,
describeConflicts,
validateReschedule,
validateBreakBlock,
breakBlockToOverride,
resolveKioskProviders,
} from "@thebookingkit/core";
// Validate a partial settings object before saving
const { valid, errors } = validateKioskSettings({ slotHeightPx: 10 });
// => { valid: false, errors: ["slotHeightPx must be between 20 and 200."] }
// Merge provider settings over org defaults
const settings = resolveKioskSettings(providerSettings, orgDefaults);
// Find overlapping bookings for a proposed time range
const conflicts = findConflicts(existingBookings, newStart, newEnd, excludeBookingId);
// Check whether a booking status allows rescheduling
const reschedulable = canReschedule("confirmed"); // true
const notReschedulable = canReschedule("completed"); // false
// Human-readable conflict summary
const message = describeConflicts(conflicts);
// => "2 conflicts: Jane Smith (2:00 PM – 3:00 PM), Walk-in (3:15 PM – 3:45 PM)"
// Full reschedule validation
const result = validateReschedule(
"confirmed",
rules,
overrides,
existingBookings,
newStart,
newEnd,
bufferBefore,
bufferAfter,
);
// => { valid: false, reason: "conflict", conflictDetails: { bookingId, startsAt, endsAt } }
// Validate a break block before creating it
const check = validateBreakBlock(
{ title: "Lunch", startTime, endTime, blockType: "break", recurring: false },
existingBookings,
);
// => { valid: true, conflictingBookings: [] }
// Convert a break block to an availability override for DB storage
const override = breakBlockToOverride(block, "America/New_York");
// => { date, isUnavailable: true, startTime: "12:00", endTime: "13:00" }
// Filter and annotate providers for the resource view
const providers = resolveKioskProviders(allProviders, ["prov-1", "prov-2"]);
// => providers with visible: true/false based on visibleIds
FunctionSignatureDescription
validateKioskSettings(settings)Validate a partial KioskSettings object; returns { valid, errors[] }
resolveKioskSettings(providerSettings?, orgDefaults?)Deep-merge provider and org settings over DEFAULT_KIOSK_SETTINGS; provider values win
findConflicts(existing, newStart, newEnd, excludeId?)Detect bookings that overlap [newStart, newEnd) using half-open interval math; inactive statuses are excluded automatically
canReschedule(status)Return true if status is "confirmed" or "pending"
describeConflicts(conflicts, formatTime?)Produce a human-readable conflict summary string; optionally supply a custom time formatter
validateReschedule(bookingStatus, rules, overrides, existingBookings, newStart, newEnd, bufferBefore?, bufferAfter?)Full reschedule pre-flight: checks status, availability, and buffer conflicts; returns RescheduleValidation
validateBreakBlock(block, existingBookings)Check a break/block against active bookings; returns { valid, conflictingBookings }
breakBlockToOverride(block, providerTimezone?)Convert a BreakBlockInput to an AvailabilityOverrideInput ready for DB storage; defaults timezone to "UTC" — always pass the provider’s actual timezone
resolveKioskProviders(providers, visibleIds?)Annotate each provider with visible: true/false; if visibleIds is omitted, all providers are visible
reasonMeaning
"conflict"Proposed slot overlaps an existing booking
"buffer_conflict"Proposed slot falls inside another booking’s buffer window
"outside_availability"Proposed slot is not within the provider’s availability rules
"blocked_date"An override marks the date as unavailable
"invalid_status"The booking’s current status does not permit rescheduling

All error classes carry a code string property that can be used for programmatic error handling without string-matching the message.

import {
// Core booking errors
BookingConflictError,
ResourceUnavailableError,
SerializationRetryExhaustedError,
UnauthorizedError,
ForbiddenError,
// Parsing & validation
InvalidRRuleError,
InvalidTimezoneError,
EventTypeValidationError,
PaymentValidationError,
EmbedConfigError,
RoutingFormValidationError,
// Seats & recurring
SeatError,
RecurringBookingError,
// Walk-in queue
WalkInsDisabledError,
QueueEntryNotFoundError,
InvalidQueueTransitionError,
} from "@thebookingkit/core";
ClasscodeAdditional propertiesThrown when
BookingConflictErrorBOOKING_CONFLICTDB EXCLUDE constraint fires; slot was taken by a concurrent booking
ResourceUnavailableErrorRESOURCE_UNAVAILABLEreason: "no_capacity" | "no_matching_type" | "all_booked"No resource can be assigned to the booking
SerializationRetryExhaustedErrorSERIALIZATION_RETRY_EXHAUSTEDAll serializable-transaction retries failed (SQLSTATE 40001 × 3)
UnauthorizedErrorUNAUTHORIZEDstatusCode: 401Request carries no valid session or token
ForbiddenErrorFORBIDDENstatusCode: 403Session exists but user lacks permission for the resource
InvalidRRuleErrorINVALID_RRULEparseRecurrence receives a malformed or unsupported RRULE string
InvalidTimezoneErrorINVALID_TIMEZONEAn unrecognised IANA timezone identifier is passed to a timezone utility
EventTypeValidationErrorEVENT_TYPE_VALIDATIONvalidateEventType finds invalid or conflicting event type configuration
PaymentValidationErrorPAYMENT_VALIDATIONCancellation policy or payment config fails validation
EmbedConfigErrorEMBED_CONFIGvalidateEmbedConfig rejects invalid embed configuration
RoutingFormValidationErrorROUTING_FORM_VALIDATIONvalidateRoutingForm or validateRoutingResponses fails
SeatErrorSEAT_ERRORSeat reservation violates capacity constraints
RecurringBookingErrorRECURRING_BOOKINGRecurring series configuration or occurrence generation fails
WalkInsDisabledErrorWALK_INS_DISABLEDWalk-in is attempted while provider’s acceptingWalkIns flag is false
QueueEntryNotFoundErrorQUEUE_ENTRY_NOT_FOUNDReferenced walk-in queue entry ID does not exist
InvalidQueueTransitionErrorINVALID_QUEUE_TRANSITIONvalidateQueueTransition called with a disallowed from → to pair