Visos online booking
Embed the booking widget
One script tag puts the business's booking flow on any website: inline in the page or as a pop-up. The business's tenant key (vb_…) and the allowed website origins are set in Visos under Settings › Online booking. The widget only loads on an allowed origin.
Snippet
Inline: the widget appears where the element is, and grows with its content.
<div data-visos-booking data-mode="inline"></div>
<script src="https://book.visos.app/embed/v1.js" async
data-visos-booking data-tenant="vb_xxxxxxxxxxxx"
data-locale="nl"></script>Pop-up: any element with data-mode="popup" opens the widget full screen on phones and as a centred sheet on larger screens. Escape or a click on the backdrop closes it.
<button data-visos-booking data-mode="popup" data-service="svc_eye_exam">Book an eye exam</button>
<script src="https://book.visos.app/embed/v1.js" async data-visos-booking data-tenant="vb_xxxxxxxxxxxx"></script>A script tag without any other [data-visos-booking] element mounts an inline widget right after itself.
Attributes
Set on the script tag they are the defaults for the page; set on an element they apply to that element.
| Attribute | Meaning |
|---|---|
| data-tenant | The business's tenant key, vb_ followed by 12 characters. Required. |
| data-mode | inline (default) or popup. |
| data-store | A store id: skips the store step when the business offers that store. |
| data-service | A service id: skips the service step when the business offers that service. |
| data-locale | nl, en, … — used when the business supports it, else its default language. |
| data-label | Popup on an empty element: the text of the button the loader creates. |
| data-title | The accessible title of the iframe and the pop-up. |
JavaScript API
// Mount elements added after the page loaded.
VisosBooking.init();
// Open the pop-up from your own code; prefill is shown to the customer, who can change it.
const booking = VisosBooking.open({
store: "…", service: "…",
prefill: { name: "Jane Doe", email: "[email protected]", phone: "+31612345678" },
});
booking.close();
// Listen; the returned function stops listening.
const stop = VisosBooking.on("booked", ({ bookingId, startsAt, endsAt, storeId, serviceId }) => { … });Events
| Event | When | Payload |
|---|---|---|
| ready | The widget loaded its configuration. | { mode } |
| step | The customer moved to another step. | { step }: store, service, time, identify, verify, details, confirm, done |
| booked | The appointment was booked. | { bookingId, startsAt, endsAt, storeId, serviceId } |
| closed | The pop-up was closed. | {} |
The loader also dispatches a DOM event that bubbles from the widget's element (or the document for a pop-up):
document.addEventListener("visos:booking-completed", (event) => {
const { bookingId, startsAt, storeId } = event.detail;
});The widget and the page talk through postMessage with the envelope { source: "visos-booking", type, … }; the loader accepts messages only from the widget origin and the frames it created.
Headless
To build your own booking screens instead, use the headless API with the business's publishable key (pk_…, Settings › Online booking) through the @optics/booking-sdk package: configuration, availability, holds, bookings and customer sign-in. The publishable key works only from the allowed origins.