Subchapter 1.55
references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.mdMarkdown8 KBView on GitHub
The Availability Time Slots Configuration SPI lets you customize how Wix calculates available time slots for booking services: duration, split interval, buffer time, locations, and required/available resources. Wix calls your listAvailabilityTimeSlotConfigurations handler whenever a customer views available times or books a service. Resources (opens in a new tab) must already exist before time slot calculation runs.
resourceTypes[].resourceTypeId must be a real resource type ID or Wix rejects every availability query for the service — confirmed live: a made-up GUID here 500s List Availability Time Slots and fails booking creation with FAILED_VALIDATING_AVAILABILITY, for every service on the site, not just the one you’re configuring. If the service is staffed (the common case), use the universal Wix Bookings staff-member resource type ID, which is the same across every site — it is not something you provision or look up per site:
const STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';For a non-staff resource (a room, piece of equipment, etc.), get the real ID from Get Resource (opens in a new tab) (resource.typeId) — never fabricate one.
resourceIds (“IDs of resources available for booking within this resource type… may be a subset of all resources in the type”) tells Wix which resources of that type are valid for this specific service. Omitting it (relying on resourceTypeId alone) was part of the same live 500 chain above — once resourceTypeId and scheduleId were both fixed, availability queries still failed until resourceIds was populated too. Get the service’s actual assigned staff from Get Service (opens in a new tab)‘s service.staffMemberIds.
The response schema requires scheduleId (confirmed live: leaving it out 500s the same as a bad resourceTypeId), but it’s easy to miss since it’s not mentioned in the request/response summary above. Fetch it from the service itself — Get Service (opens in a new tab)‘s service.schedule._id (REST docs call this field id; the SDK’s Service.schedule type names it _id):
import { auth } from "@wix/essentials";
import { services } from "@wix/bookings";
const elevatedGetService = auth.elevate(services.getService);
const service = await elevatedGetService(serviceId);
const scheduleId = service.schedule?._id;The schema notes _id is “returned only for business locations” — read that as required whenever locationType: BUSINESS, not optional. Omitting it was the third and last piece of the same live 500 chain. Get the service’s actual configured locations from Get Service (opens in a new tab)‘s service.locations[] (each has its own _id) rather than hardcoding a bare { locationType: "BUSINESS" }.
None for activation — Wix calls this plugin automatically once installed and released. But get all three fields above right, or every availability query for every service on the site fails with a bare 500, not just a validation error on the one service you’re configuring.
Before implementing, call ReadFullDocsMethodSchema on the docs URL to get the full request/response types.
| Handler | Docs URL |
|---|---|
listAvailabilityTimeSlotConfigurations | https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/availability-time-slots-configuration-service-plugin/list-availability-time-slot-configurations?apiView=SDK (opens in a new tab) |
The request’s services array (up to 100 entries) lists each service to configure, with the customer’s customerChoices (selected duration variant and/or add-ons) — the same service GUID can appear more than once with different choices. Your response’s configurations array must contain exactly one configuration per requested entry, in the same order, with resourceTypes, locations, and scheduleId fully populated.
For services with a bookable duration range (the customer picks how long to book), return durationUnit (HOUR or DAY) with minDuration/maxDuration instead of a fixed duration; for hourly ranges also return duration.defaultInMinutes as the minimum.
import { availabilityTimeSlotsConfiguration } from "@wix/bookings/service-plugins";
import { auth } from "@wix/essentials";
import { services } from "@wix/bookings";
// Universal Wix Bookings staff-member resource type ID — same on every site, not per-site data.
const STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
availabilityTimeSlotsConfiguration.provideHandlers({
listAvailabilityTimeSlotConfigurations: async (payload) => {
const { request } = payload;
// services is optional on the request type — default it, or the .map()
// calls below fail `tsc` with "possibly undefined."
const { services: requestedServices = [] } = request;
// scheduleId is required in the response but isn't in the request — fetch it per service.
const elevatedGetService = auth.elevate(services.getService);
const serviceById = new Map(
await Promise.all(
requestedServices.map(async ({ serviceId }) =>
[serviceId, serviceId ? await elevatedGetService(serviceId) : undefined] as const
)
)
);
const configurations = requestedServices.map(({ serviceId, customerChoices }) => {
const baseDuration = 60;
const addOnMinutes = (customerChoices?.addOnIds ?? []).length * 15;
const service = serviceById.get(serviceId);
return {
serviceId,
resourceTypes: [
{
resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID,
resourceIds: service?.staffMemberIds ?? [],
numberOfResourcesRequired: 1,
},
],
locations: (service?.locations ?? []).map((location) => ({
_id: location._id,
locationType: availabilityTimeSlotsConfiguration.LocationType.BUSINESS,
})),
splitIntervalInMinutes: baseDuration + addOnMinutes,
duration: { defaultInMinutes: baseDuration + addOnMinutes },
bufferTimeInMinutes: 10,
scheduleId: service?.schedule?._id,
};
});
return { configurations };
},
});services, even when the same service GUID repeats with different customerChoices.addOnIds over durationInMinutes in the request — when both are present they must agree, so let Wix Bookings compute total duration from add-ons rather than passing a duration you’d have to keep in sync.splitIntervalInMinutes vs duration — when the split interval matches the service duration, splitIntervalInMinutes is duration.defaultInMinutes + bufferTimeInMinutes.durationUnit for services where the customer picks the length of the booking; leave duration empty for DAY-unit ranges.services is optional on the request type — default it to [] when destructuring, or the .map() calls fail tsc with “possibly undefined.”