Installing the booking widget

One script tag. No plugin, no build step, nothing to change on your server.

<script src="https://booking.lazavee.com/v1/widget.js"
        data-key="pk_live_your_key"
        defer></script>

That is the whole installation. With nowhere else to put it, the widget adds a floating Book now button that opens the booking flow in a dialog.

Your key is in the dashboard under Widget. It is public by design: it sits in the source of your own website, and we assume anybody can read it. It permits three things and nothing else — reading your public booking configuration, reading which times are free, and creating a booking.

Before you paste it

Register the website it will run on, in the dashboard. A live key works only on the addresses you name.

https://salon.com              one exact site
https://*.salon.com            any single subdomain: shop.salon.com, book.salon.com

https://salon.com and https://www.salon.com are different addresses. Register both if your site answers on both. So are http:// and https://, and so are two different ports.

While you are building, use a test key (pk_test_…). It works from anywhere, including localhost and preview deployments, and its bookings are marked as tests: no real email is sent and they stay out of your reports.

Putting it in a particular place

<div id="booking"></div>

<script src="https://booking.lazavee.com/v1/widget.js"
        data-key="pk_live_your_key"
        data-target="#booking"
        defer></script>

The widget fills the width of that element and sets its own height as the customer moves through the booking. Give the container a max-width if you want it narrower; do not give it a fixed height.

AttributeWhat it does
data-keyRequired. Your key
data-targetCSS selector for the element to render into
data-serviceSkip the list and go straight to one service
data-staffPre-select a staff member
data-dateOpen the calendar on this date (2026-10-02)
data-modeinline or modal
data-labelText for the floating button
data-localeOverrides the language set on the key

Several on one page

Any element with data-earlybillit-booking becomes a place the widget renders:

<div data-earlybillit-booking data-service="svc_9k3mQx"></div>
<div data-earlybillit-booking data-service="svc_2p8Lzr"></div>

This is also the React, Vue and Svelte path. The loader watches the page, so it does not matter whether your framework draws the element before or after the script runs, and a component that disappears and comes back is picked up again.

Driving it from your own code

EarlyBillIt.open({ service: 'svc_9k3mQx' });   // open the dialog
EarlyBillIt.close();
EarlyBillIt.mount('#container', { service: 'svc_9k3mQx' });
EarlyBillIt.destroy();                          // remove everything

If your script might run before the widget has loaded, queue it instead. This works from anywhere on the page, including a tag manager:

window.earlyBillItQueue = window.earlyBillItQueue || [];
earlyBillItQueue.push(['open', { service: 'svc_9k3mQx' }]);

Knowing when somebody books

EarlyBillIt.on('booking.confirmed', ({ reference, service, startsAt, amount, currency }) => {
  window.dataLayer?.push({ event: 'purchase', transaction_id: reference, value: amount / 100 });
});

EarlyBillIt.on('step.changed', ({ step, index, total }) => { /* funnel tracking */ });
EarlyBillIt.on('error', ({ code }) => { /* something was refused */ });

These carry no personal information, and that is deliberate. You get a reference, a service, a time and an amount — never the customer's name, email or phone number. You want to measure conversions; you do not want your customers' details handed to an analytics tag by a script you did not write, and we are not willing to be the thing that does it. When you need those details they are in your dashboard and in the email we send you, where who can see them is your decision.

Particular tools

WordPress. Paste it into a Custom HTML block, or into the theme footer under Appearance → Theme File Editor, or a snippet plugin. There is no Lazavee plugin and none is needed.

Squarespace, Wix, Shopify. Paste it wherever the site lets you add custom HTML or a footer script.

React or Next.js. Load it once and place the element:

export default function Booking({ service }) {
  useEffect(() => {
    if (document.getElementById('eb-loader')) return;
    const s = document.createElement('script');
    s.id = 'eb-loader';
    s.src = 'https://booking.lazavee.com/v1/widget.js';
    s.dataset.key = 'pk_live_your_key';
    s.defer = true;
    document.body.appendChild(s);
  }, []);

  return <div data-earlybillit-booking data-service={service} />;
}

In Next.js, <Script src="…" strategy="afterInteractive" data-key="…" /> does the same. Strict mode drawing everything twice is fine; the loader will not render twice into one element.

If nothing appears

A box saying "not authorised for this website". The key is valid but this address is not registered. The message names the exact address we saw — copy it into the dashboard, or check whether you registered salon.com and published to www.salon.com.

A box saying "unavailable". The key is unknown or has been revoked.

Nothing at all, and an error in the browser console mentioning frame-src. Your own site has a Content-Security-Policy. Add:

frame-src https://booking.lazavee.com;
script-src https://booking.lazavee.com;

Nothing else is needed: the booking flow's own requests happen inside our frame, on our address, so your connect-src is not involved.

It appears but the height looks wrong. Check that the container has no fixed height and no overflow: hidden.

What actually runs on your site

The loader is about 3 KB and does four things: it creates the frame, tells it your page's address and whether your site is in dark mode, applies the height the frame asks for, and passes those events back to you. It reads nothing else from your page and sends nothing anywhere. Everything a customer types is typed inside our frame, on our address, where your site cannot read it and neither can anything else running on your page.

Stuck? Write to [email protected].