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.
No website? You do not need one
Every business also gets a booking page of its own, at an address like
https://lazavee.com/book/your-business. It shows
your services and takes bookings exactly like the widget does — there is simply nothing to
install. Put the link on your Facebook Page's Book Now button, in your bio,
in a pinned post, or in a Messenger reply.
The same page has a QR code, in your dashboard under the link. Print it on a tarpaulin outside the shop, a poster, a flyer, a receipt, a table card, or the mirror in front of the chair: anyone who scans it lands on your booking page, with no typing and no app to download. Download the PNG for posting online and the SVG for a print shop, where it stays sharp however large it is printed.
You can change that address later from your settings. The old one keeps working, so a link already printed on a sign does not stop working the day you rename.
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-color | Button colour, as #RRGGBB |
data-size | small, medium or large |
data-shape | pill, rounded or square |
data-locale | Overrides the language set on the key |
The button's text, colour, size and shape are also in your dashboard, under Widget. Setting them there changes every site running your snippet within about five minutes, with nothing to re-paste. The attributes above win where both are set, so a site that wants its own colour can keep it.
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].