RECIPE: Reserve a Table or Book an Experience

When to use this recipe

  • "Table for 4 tomorrow at 7
    pm, I'm Noa, phone …"
  • "What's the largest group I can book for?" / "Are you open for reservations on Sundays?"
  • "Tell me about the Sunday brunch table — when is the next one, how much per person?"
  • "Book 2 seats at the next brunch for Maya…"

Inputs you need before STEP 3

InputHow to get it
Visitor tokenSTEP 1
Business timezoneGetBusinessDetailstimeZone
reservationLocationId, partySize.min/max, businessSchedule.periods, approval.modeSTEP 2
Requested date + time converted to UTC in the business zone; party size within boundsVisitor + date rules
Guest firstName and phone (required), optional last name/emailAsk the visitor — do not fabricate
Experience id, per-guest price, weekly schedule (experiences)STEP 5

Decision tree

  • "How many / open on Sunday?" → STEP 1 + STEP 2 (one read answers both).
  • "Table for N on at → STEP 2 → STEP 3 (slots) → STEP 4 (hold → reserve, or single-shot create).
  • "Tell me about " → STEP 5; compute the next occurrence from the weekly schedule.
  • "Book seats at " → STEP 5 → STEP 4b (create reservation with experienceId).
  • On 428 MUST_BE_PREMIUM at any write: stop, explain online reservations aren't enabled on this site, give phone/email from GetBusinessDetails.

Date and time rules

  • Resolve "tomorrow / Friday / 7
    pm" in the business timezone, then convert to UTC for date/startDate (2026-09-17T19:30:00+03:002026-09-17T16:30:00Z). Sending the local clock time with a Z suffix asks for the wrong hour (a 19
    request became 22
    local and returned NON_WORKING_HOURS).
  • Slot granularity is timeSlotInterval (15 min here); ask for slotsBefore/slotsAfter to offer neighbours.
  • Only status: "AVAILABLE" slots are bookable; NON_WORKING_HOURS and UNAVAILABLE are not.
  • A held reservation expires in 10 minutes; collect the guest details before holding.

STEP 1: Get a visitor token

GenerateVisitorToken once; reuse it.


STEP 2: Read the reservation location

GET https://www.wixapis.com/table-reservations/reservation-locations/v1/reservation-locations (visitor-readable).

Copy

STEP 3: Find free times

POST https://www.wixapis.com/table-reservations/reservations/v1/time-slots

Copy

{ "timeSlots": [{ "startDate": "2026-09-17T16:30:00Z", "duration": 90, "status": "AVAILABLE", "manualApproval": false }, …] }. Offer the AVAILABLE ones in local time. check-time-slot (same base, /check-time-slot) additionally requires duration — prefer time-slots.


STEP 4: Hold, then confirm

Copy

Single-shot alternative (also used for experiences): POST https://www.wixapis.com/table-reservations/reservations/v1/reservations

Copy

On this (free-plan) site both hold and reservations return 428 { "message": "site must be premium", "details": { "applicationError": { "code": "MUST_BE_PREMIUM" } } }. That is the expected gate — reading slots works, writing does not.


STEP 5: Experiences (brunch table, chef's table…)

POST https://www.wixapis.com/table-reservations/experiences/v1/experiences/query with { "query": {} } (visitor-readable).

Copy

There is no experience-scoped availability endpoint: compute the next occurrences yourself from weeklyOptions.startDaysAndTimes (+ startDate/endDate, oneTimeOptions) in the business timezone, honouring minimumReservationNotice. Price is per guest in the site currency. Booking uses STEP 4b with details.experienceId; it is premium-gated like tables.


Common errors and how to avoid them

1. 428 MUST_BE_PREMIUM ("site must be premium") on hold / reserve / create

Online reservations need a premium plan even though the location reads onlineReservationsEnabled: true. Don't retry; give the visitor the phone and email.

2. Every slot is NON_WORKING_HOURS

You sent local time with a Z. Convert to UTC from the business timezone first.

3. 400 duration must not be empty from check-time-slot

Use time-slots (no duration needed) or pass duration in minutes.

4. 404 on /table-reservations/v1/reservations/held or /table-reservations/v1/reservations

The base is /table-reservations/reservations/v1/…; hold is /reservations/hold, confirm is /reservations/{id}/reserve, create is /reservations.

5. Reservee rejected

firstName and phone are required for every online reservation; use a real E.164 number from the visitor.

6. Expecting an experience "time slots" call

None exists; project the schedule (STEP 5).


Constants

ConstantValue
Table Reservations app idf9c07de2-5341-40c6-b096-8eb39de391fb
Reservations basehttps://www.wixapis.com/table-reservations/reservations/v1/
Held reservation lifetime10 minutes

References

Last updated: 17 September 2026

Did this help?