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.
| Attribute | What it does |
|---|---|
data-key | Required. Your key |
data-target | CSS selector for the element to render into |
data-service | Skip the list and go straight to one service |
data-staff | Pre-select a staff member |
data-date | Open the calendar on this date (2026-10-02) |
data-mode | inline or modal |
data-label | Text for the floating button |
data-locale | Overrides 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].