Velo Tutorial: Creating a Custom Bookings Experience

Visit the Velo by Wix website to onboard and continue learning.

Using the Velo Bookings API you can create a custom booking experience for the services you offer on your site.

Prerequisites

Before working with the Bookings API, set up your services using the Bookings App.

If you will be taking payments for bookings, you need to set up your site to accept payments before using the Bookings API. To learn more, see About Accepting Payments.

To check out bookings with the Bookings API you need to upgrade to a Business Premium Plan.

Note: When setting up your site to accept payments, be sure to select the payment methods you want to offer and set your payment currency.

Bookings API

The Bookings API consists of two client-side functions used to get a service's available slots and to book one of the available slots:

  • getServiceAvailability() - Called to retrieve available slots for a given service. Before calling this function you must first retrieve the ID of the service to be booked.
  • checkoutBooking() - Called to book a service and to prompt the current site visitor to enter payment information if required. This function requires you to pass the specific service slot to be booked and values for any form fields that are needed when booking the service.

For detailed information on the Bookings API see wix-bookings-frontend in the API Reference.

Bookings Lifecycle

The following list outlines the steps taken in a standard booking lifecycle to demonstrate how the Bookings API can be used. Remember, you do not need to follow this flow. You can use the Bookings API to create a custom booking flow that meets your site's specific needs.

  1. You get a list of services from the Bookings/Services collection and display them.
  2. A user selects one of the displayed services.
  3. You call the getServiceAvailability() function using the selected service's Service ID (_id) value. You get that ID from the results of the query performed above. (Optionally, you can pass a ServiceAvailabilityOptions object to change the limits on the slots that are returned.)
  4. You display the returned slots. You may also want to retrieve the staff member items that relate to the returned slots from the Bookings/Staff collection.
  5. You also display input elements to gather the service's form fields if there are any. You retrieve the list of form fields from results of the query performed above in the fields property.
  6. A user enters values for the form fields and indicates that the booking should be checked out.
  7. You call the checkoutBooking() function. You pass the selected slot object, the values for the form fields, and the payment type if neccessary. Note, the specified payment type must match the service's configuration in your site's Dashboard. You cannot book a paid service as if it were free.
    • If the service is free, you do not need to pass a paymentOptions object.
    • If the service is not free and you pass a paymentOptions object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information.
    • If the service is not free and you pass a paymentOptions object indicating the payment should be offline, the payment popup is not presented to the user.

Bookings Example

The following is an example that shows how to use page elements and code to achieve the sample bookings lifecycle described above. The example steps correspond to the numbers in the list above.

Note: In this example, we focus on the code that drives the bookings lifecycle. We do not include any additional code that helps guide the user through the booking process. You might want to split the lifecycle into sections on your page and expand or collapse the sections depending on what is relevant for the user at a given point  in the lifecycle.

Step 1: Retrieve and Display Services

You can retrieve your site's list of services and display them in a number of ways. In this example, we use a dataset connected to a repeater. This allows us to fully customize what service data is displayed and how we display it using the least amount of code.

Another option for retrieving the service list is:

  • Query your site's Bookings/Services collection. 

Other options for displaying the service list are:

  • Table
  • Gallery
  • Dropdown
  • Radio Buttons
  • Checkboxes

In our example, we add a dataset with the ID bookingsDataset connected to the Bookings/Services collection. The following page elements are connected to fields in the collection through the dataset:

TypeIDConnected to field
RepeaterservicesRepeater-
TexttitleTextService Name
ImageserviceImageService Image
TexttaglineTextService Tagline
TextpriceTextPrice Summary
ButtonbookButton-

Step 2: User Selects Service

Depending on how you choose to display your services, you will need to react to a user's selection of a service differently. The bottom line is that you have to get the ID of the service that the user selected.

In our example, the user clicks a button in a repeater connected to dataset, so we can use the button's click event to get the ID of the selected service.

We add an onClick event handler to the bookButton using the Properties & Events Panel. 

Copy
1
export function bookButton_click(event) {
2
}

Step 3: Retrieve Available Slots

You retrieve the selected service's available slots by calling getServiceAvailability(). The function requires that we pass it the ID of the selected service.

In our example, we've already gotten the ID of the selected service. So all we need to do is use it when calling getServiceAvailability().

First, we need to import the Bookings API all the way at the top of our page's code.

Copy
1
import wixBookingsFrontend from 'wix-bookings-frontend';

Then, we can add the function call to the event handler we created in the previous step. We need to store the slot information for later, so we also create the following variables:

  • availableSlots
  • slotOptions
  • selectedSlot
  • slotIndex
Copy
1
let availableSlots;
2
let slotOptions;
3
let selectedSlot;
4
let slotIndex;
5
6
export function bookButton_click(event) {
7
8
wixBookingsFrontend.getServiceAvailability(event.context.itemId)
9
.then( (availability) => {
10
availableSlots = availability.slots;
11
} );
12
}

When calling getServiceAvailability(), you can optionally pass an object that defines a datetime range that refines which slots will be returned.

Step 4: Display Available Slots

Once again, you can display the data we just retrieved in a number of ways. In this example, we use a table to display all the available slots. We have to do a little processing of the slot data to get it into the right format for the table. 

Our table's ID is slotTable, it has only 1 column whose Field Name is slotDate.

Again, we add some code to the event handler created above. We store the slot options in the slotOptions array, although we only display the slotDate property in the table. We'll use the ID property later in Step 8. 

Copy
1
export function bookButton_click(event) {
2
3
wixBookingsFrontend.getServiceAvailability(event.context.itemId)
4
.then( (availability) => {
5
availableSlots = availability.slots;
6
//---Added in this step---//
7
slotOptions = availableSlots.map((slot) => {
8
let date = slot.startDateTime;
9
return {
10
"slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(),
11
}
12
});
13
$w('#slotTable').rows = slotOptions;
14
//---End of added in this step---//
15
} );
16
}

Step 5: Store the User's Selected Slot

We'll need to know which slot the user selected in the table. To do that we need to go to the table setting and set Clicking selects to Rows. Then we'll use the Properties & Events Panel to add an onRowSelect event to the table. 

When the user clicks on a row we want to store the slot information for their selected row, so we'll add code to the event handler:

Copy
1
export function slotTable_rowSelect(event)
2
selectedSlot = event.rowData;
3
slotIndex = event.rowIndex;
4
}

Step 6: Display Form Fields

We also need to display input elements to gather the selected service's form fields. Here we use a repeater with the ID formFieldRepeater. Each item in the repeater consists of a single text input with the ID fieldInput. Each item in the repeater will be used to collect one of the service's form fields. Here, we also choose to only collect the required information.

First, we add some code to the event handler created above.

Copy
1
export function bookButton_click(event) {
2
3
wixBookingsFrontend.getServiceAvailability(event.context.itemId)
4
.then((availability) => {
5
availableSlots = availability.slots;
6
slotOptions = availableSlots.map((slot) => {
7
let date = slot.startDateTime;
8
return {
9
"slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(),
10
}
11
});
12
$w('#slotTable').rows = slotOptions;
13
});
14
15
//---Added in this step---//
16
let $item = $w.at(event.context);
17
let formFields = $item("#bookingsDataset").getCurrentItem().form.fields;
18
19
formFields = formFields.filter(field => field.constraints.required)
20
21
$w('#formFieldRepeater').data = formFields;
22
//---End of added in this step---//
23
} );
24
}

Then, we add an event handler for the repeater that sets the placeholder text in each of the text input elements.

Copy
1
export function formFieldRepeater_itemReady($item, itemData, index) {
2
$item("#fieldInput").placeholder = itemData.label;
3
}

The repeater looks like this in the editor (there is an input element inside the repeater):

And it looks like this on the site when populated:

Step 7: User Enters Form Field Values and Books Service

At this point, the user enters values for the form fields in the repeater we just set up. Then the user indicates that we should process the booking. In our example, we use a button with the ID checkoutButton for this purpose.

We add an onClick event handler to the checkoutButton using the Settings Panel. Here we'll collect all of the data that the user entered into the form fields.

Copy
1
export function checkoutButton_click(event) {
2
let formFieldValues = []; // list of values user entered
3
4
// for each item in the repeater
5
$w('#formFieldRepeater').forEachItem( ($item, itemData, index) => {
6
// add an object containing the corresponding form field's
7
// ID and the value that the user entered
8
formFieldValues.push( {
9
"_id": itemData._id,
10
"value": $item("#fieldInput").value
11
} );
12
} );
13
}

Step 8: Booking Checkout

You perform a booking checkout by calling checkoutBooking(). The function requires that we pass it the selected slot object and the form field values that the user entered. We package these together in an object named bookingInfo. Remember, we've already collected the form field data in a variable named formFieldValues.

When calling checkoutBooking(), you can optionally pass an object that defines the payment options. In our example, we are using services which are paid for offline, so we will not be passing any payment options.

We also handle the result returned by the checkout. We display the checkout status to the user in a text element with the ID confirmationText.

Copy
1
export function checkoutButton_click(event) {
2
let formFieldValues = [];
3
4
$w('#formFieldRepeater').forEachItem( ($item, itemData, index) => {
5
formFieldValues.push( {
6
"_id": itemData._id,
7
"value": $item("#fieldInput").value
8
} );
9
} );
10
11
//---Added in this step---//
12
// build bookingInfo object
13
let bookingInfo = {
14
// selected slot object
15
"slot": availableSlots[slotIndex],
16
// form filed values collected above
17
"formFields": formFieldValues
18
};
19
20
// booking checkout
21
wixBookingsFrontend.checkoutBooking(bookingInfo)
22
.then( (results) => {
23
$w("#confirmationText").text = `Booking ID: ${results.bookingId} Status: ${results.status}`;
24
} );
25
//---End of added in this step---//
26
}

When a checkout is performed, the user is presented with an experience that reflects the service's payment type.

  • If the service is free, the checkout is booked without any other input.
  • If the service is not free and you pass a paymentOptions object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information.
  • If the service is not free and you pass a paymentOptions object indicating the payment should be offline, the payment popup is not presented to the user.
Was this helpful?
Yes
No