Any visitor request about a Wix Bookings service catalog, availability, or making an appointment. Examples:
| Input | How to get it |
|---|---|
| Visitor token | STEP 1 (GenerateVisitorToken, or reuse one already in the conversation) |
| Business timezone | GetBusinessDetails tool if the MCP has it, otherwise the timeZone field of any STEP 3 response |
Service GUID, schedule.id, form.id, type, duration, payment.options | STEP 2 (POST /bookings/v2/services/query) |
| Staff resource GUID (only if the user named a person) | STEP 2 with conditionalFields: ["STAFF_MEMBER_DETAILS"] → staffMemberDetails.staffMembers[].staffMemberId |
A bookable slot (localStartDate, localEndDate, scheduleId, location, availableResources) | STEP 3 (POST /_api/service-availability/v2/time-slots) |
| Fresh confirmation the slot is still open | STEP 4 (POST /_api/service-availability/v2/time-slots/get) |
| Booking form field keys and the visitor's answers | STEP 5 (GET /form-schema-service/v4/forms/{formId}/summary) + ask the visitor |
timeSlotsPerDay: 1, then drill into one day.resourceIds if staff named) → STEP 5 (collect details) → STEP 4 (re-validate) → STEP 6 → STEP 7 if paying online.This recipe covers appointment services (type: "APPOINTMENT"), the common case. For CLASS use List Event Time Slots and book with bookedEntity.slot.eventId; for COURSE book with bookedEntity.schedule.scheduleId (see References).
Apply these before STEP 3 on every request.
timeZone in STEP 3 and Wix returns slots in it.08:00-12:00, midday 11:00-14:00, afternoon 12:00-17:00, evening 17:00-21:00, unspecified 00:00-23:59. If the window is empty, widen to the whole day and say so.fromLocalDate = <date>T14:00:00, toLocalDate = <date>T14:01:00 and check the returned localStartDate equals the requested time. Slot granularity is per service (15/30/60 min); if the exact minute is not offered, return the two nearest slots.2026-09-18T14:00:00) plus an IANA timeZone; any offset in the string is ignored when timeZone is set. Create Booking takes absolute times (2026-09-22T18:00:00.000Z or 2026-09-22T11:00:00.000-07:00). Convert local → absolute per date (DST changes the offset), and keep endDate - startDate equal to the service duration.tooEarlyToBook / tooLateToBook are excluded by bookable: true. Never say a time is free without a fresh STEP 3 or STEP 4 call.If you do not already have a visitor token in the conversation context, call the GenerateVisitorToken tool to mint one. Reuse the same token for every step — the booking, the checkout and the checkout URL must all be created by the same visitor identity.
POST https://www.wixapis.com/bookings/v2/services/query
WARNING — $contains is not a supported filter operator on this endpoint. Sending "filter": { "name": { "$contains": "massage" } } returns:
$startsWith works but is case-sensitive. For anything fuzzy ("do you do massage?", "haircut", "the couples one") fetch all non-hidden services and match name, tagLine, description and category.name client-side, case-insensitive, with synonyms.
List the catalog with staff (use this by default):
If pagingMetadata.total > 100, repeat with paging.offset.
staffMemberDetails.staffMembers[].staffMemberId is the resource GUID used by availability (resourceIds) and booking (resource.id). No separate staff query needed. If a service lacks staff details, POST /bookings/v1/staff-members/query with {"query":{}} works for visitors — use its resourceId, not id.POST https://www.wixapis.com/_api/service-availability/v2/time-slots
WARNING — availableResources comes back empty unless you send includeResourceTypeIds. Without it every slot has "availableResources": [] and you cannot tell who is free. Always include the staff resource type constant 1cd44cf8-756f-41c3-bd90-3e2ffcaf1155.
Slots in a window (day part or whole day):
Variants:
fromLocalDate = the time, toLocalDate = one minute later."resourceIds": ["<STAFF_RESOURCE_GUID>"]."locations": [{ "id": "<LOCATION_GUID>", "locationType": "BUSINESS" }]."timeSlotsPerDay": 1.cursorPagingMetadata.cursors.next as cursorPaging.cursor.Empty timeSlots means nothing is open in that window (closed day, fully booked, or past). Widen the window and offer alternatives instead of answering a bare "no".
POST https://www.wixapis.com/_api/service-availability/v2/time-slots/get
Call this immediately before STEP 6, after the visitor has picked a time and given their details. It returns the full resource list for that one slot.
If bookable is false or the requested person is missing, go back to STEP 3 and offer alternatives.
GET https://www.wixapis.com/form-schema-service/v4/forms/<FORM_GUID>/summary
fields[].target are the exact JSON keys for formSubmission in STEP 6. They are snake_case (first_name), not camelCase.phone must be a valid E.164 number from a country the site allows. Reserved fake numbers (+1555010xxxx) are rejected.POST https://www.wixapis.com/bookings/v2/bookings
WARNING — camelCase form keys return 400. This body fragment fails:
Use the target keys from STEP 5 and a real phone number.
Field mapping:
| Field | Source |
|---|---|
slot.serviceId | STEP 2 services[].id |
slot.scheduleId | STEP 3 timeSlots[].scheduleId (= service schedule.id) |
slot.startDate / endDate | STEP 3 localStartDate / localEndDate converted to absolute time in the business zone |
slot.timezone | business timezone |
slot.resource | one entry from STEP 4 availableResources[].resources[]; omit the whole resource object when the visitor has no preference and Wix will assign staff |
slot.location | STEP 3 location.id + name, with locationType OWNER_BUSINESS (slots say BUSINESS; CUSTOMER and CUSTOM map unchanged) |
selectedPaymentOption | ONLINE if payment.options.online; OFFLINE if only inPerson; MEMBERSHIP for pricing-plan members. Ask when both online and offline are allowed |
formSubmission | STEP 5 target keys → visitor answers. Do not also send booking.contactDetails |
A CREATED booking does not hold the slot for the visitor until it is paid or confirmed, so run STEP 7 right away. If the call fails because the slot was just taken, return to STEP 3. If selectedPaymentOption was OFFLINE, stop here and tell the visitor the booking is submitted and (if the business approves manually) awaiting confirmation.
POST https://www.wixapis.com/ecom/v1/checkouts
WARNING — catalogItemId is the booking GUID, not the service GUID, and appId is the Wix Bookings catalog app ID, not the site's app or the service's appId field value you might see elsewhere.
Then:
Reply with the summary (service, local date and time, staff, location, total) and the checkout URL. Wix confirms the booking automatically after payment (CONFIRMED, or PENDING when the business approves manually). Unpaid CREATED bookings expire on their own.
These are the ways this flow has failed in practice. The steps warn about them inline; this section is the consolidated reference.
$contains on Query Services returns 400Resolution: drop the filter (or use hidden: false only), fetch up to 100 services, match client-side and case-insensitively across name, tagLine, description, category.name.
availableResources: []You omitted includeResourceTypeIds. Without it the slots are still correct but anonymous, so you cannot honour "with Maria" or name the stylist. Resolution: always send "includeResourceTypeIds": ["1cd44cf8-756f-41c3-bd90-3e2ffcaf1155"]; to restrict to one person also send resourceIds.
Resolution: keys are the target values from Get Form Summary (first_name, last_name, email, phone, …).
Placeholder numbers such as +15555550123 fail validation. Resolution: collect the visitor's real number in E.164 form; do not fabricate one to get past the step.
locationType on the bookingSlots report "locationType": "BUSINESS", but Create Booking expects OWNER_BUSINESS for a business location. Copy id and name from the slot and translate the type.
CREATED booking as donestatus: "CREATED" is unpaid and unconfirmed; it neither blocks the slot nor appears in the business calendar. Always continue to STEP 7 for online payment, and say "pay here to confirm" rather than "you're booked".
catalogReference.catalogItemId must be the booking GUID from STEP 6. The service GUID produces an unusable checkout. appId is always 13d21c63-b5ec-5912-8397-c3a5ddb27a97.
GET /bookings/v2/bookings/{id} → 404, POST /bookings/v2/bookings/{id}/cancel → 403 with a visitor token. Visitors cannot manage bookings through the API. Resolution: point them to the confirmation email link, the site's members area, or the business contact details from GetBusinessDetails.
| Constant | Value |
|---|---|
Wix Bookings catalog appId (checkout) | 13d21c63-b5ec-5912-8397-c3a5ddb27a97 |
| Staff member resource type ID | 1cd44cf8-756f-41c3-bd90-3e2ffcaf1155 |
| Default booking form ID | 00000000-0000-0000-0000-000000000000 |
Everything else — service, schedule, staff, location and form IDs, timezone, prices, durations — is per site. Discover it in STEP 2 and STEP 3 during the conversation; never reuse IDs from memory or from another site.
All endpoints in this recipe accept visitor or member authentication. Route every call through GenerateVisitorToken + CallWixSiteAPI / ExecuteWixAPI with the same token.
Last updated: 15 September 2026