Documentation
How the
Eazo widget works.
A clear guide for business owners - no coding needed. Adding it to your website, setting the look and more advanced tricks like a widget for a single service.
1. Adding the widget to your website
You add the widget to any page with two lines of HTML - no plugin, no installation, no developer needed. Your own code with your identifier is in the admin on the Widget settings page after you sign in; in general it looks like this:
<script src="https://cdn.eazo.eu/widget/embed.js" defer></script>
<eazo-widget data-tenant="your-identifier"></eazo-widget>
Just paste the code where the widget belongs - on its own page, in a sidebar or as a custom HTML block in WordPress, Webflow or another CMS. The widget loads asynchronously, so it doesn’t slow down the rest of the page.
2. Look and behaviour
Everything the widget shows customers is set on the Widget settings page in the admin - no code changes:
- Primary colour and theme - light, dark or following the visitor’s device.
- Language - Czech or English. On a website that declares its language (the lang attribute of html), the widget follows the page language on its own.
- “Powered by Eazo” - a small link in the footer, can be turned off on a paid plan.
- How far ahead clients can book - how many months ahead clients see free slots.
- E-mail confirmations - whether the client gets an e-mail after booking.
Need a different look on a specific page than your global settings? The same admin page also lists optional HTML attributes (language, theme, width and more) that override the settings for that embed.
3. Widget with one preselected service
Do you have a separate page on your website for one specific service (say a landing page for one treatment or consultation)? You don’t have to make clients look for the same service in the list again - the widget can have it preselected.
The ready-made code for a specific service is on its page in the admin, in the “Embed code (this service only)” section. It looks like this:
<eazo-widget
data-tenant="your-identifier"
data-service="service-identifier"></eazo-widget>
With this code the client skips choosing a service and goes straight to picking a time, but can still go back and choose another service - they see a “Change” link next to the selected service.
Want to offer really only this one service?
When the page only makes sense for one specific service (and you don’t want the client to wander off to another), add the data-lock-service attribute to the code:
<eazo-widget
data-tenant="your-identifier"
data-service="service-identifier"
data-lock-service></eazo-widget>
With this attribute, in addition:
- the “Change” link next to the selected service disappears - the client has no way to switch,
- the back button disappears too - there is nowhere to go back to from picking a time,
- the widget starts numbering from step 1, so it doesn’t look like a step is missing.
You don’t have to remember or type either variant - on the service page in the “Embed code (this service only)” section just tick “Without the option to change the service” and the ready code with data-lock-service appears. Without data-service, data-lock-service has no effect.
4. Don't have a website?
No problem. Every account also comes with a separate public booking page, found in the admin under “Public booking link”. Just share it – on social media, in your e-mail signature or on a business card – and clients book with you right there, no website needed.
5. Bringing clients back
Calendars empty out in two ways: a client doesn’t get in touch for a long time, or someone cancels the day before and the slot stays empty. Eazo has a feature for each that runs on its own. Both are part of the Pro plan.
Invitation to the next visit
For each service you can set how long after a visit Eazo should remind the client – a haircut after four weeks, a facial after two months, an annual check-up after a year. When the time comes, the client gets an e-mail with a link straight to picking a time for the same service.
Set it up in the admin on the service page in the “Invitation to the next visit” section. It is off by default, so nothing is sent until you choose an interval.
Not everyone gets the invitation. Eazo skips clients who already have another appointment booked and those who didn’t show up last time. It goes out only once per visit and the client can unsubscribe with one click – e-mails about their own bookings keep coming.
Waiting list
When a day is full, the widget offers the client a spot on the waiting list. They leave their name and contact and as soon as anything frees up that day – a client cancels, you reject a booking or someone moves it – they get an e-mail with a booking link.
Turn it on in the admin under Widget → Behaviour → “Waiting list”. You see who is waiting in the menu under Waiting list; you can also remove someone there if you arrange things by phone.
The offer goes to everyone waiting at once and whoever responds first gets the slot – it is not reserved or held for anyone. The e-mail says so plainly, so nobody expects the spot to wait for them. Anyone who misses it stays on the list for the next freed slot; they get the offer at most three times, so a run of cancellations doesn’t flood them with e-mails.
Eazo deletes sign-ups for days that have passed by itself.
6. Custom booking form
You can customise the widget’s colour, theme and language. But if you need a form that looks entirely your own (custom layout, fonts, fields matching your website), you can build it yourself and send bookings to Eazo through the same interface the widget uses. This part is for whoever builds your website.
How a booking works
- Loading the workspace –
GET https://api.eazo.eu/v1/tenants/{workspace}returns services (serviceswithid, name, duration and price), staff (staff) and the form fields you have enabled in the admin (stepsConfig). - Free slots –
GET …/services/{service}/availability/2026-10returns free times for the whole month by day,GET …/services/{service}/days/2026-10-15for a single day. Times are in the workspace’s time zone and you send them back in the same form. If nothing is free that month,nextAvailableDatein the response holds the nearest day with a free slot – you can offer it to the client right away. - Submitting –
POST …/services/{service}/bookingswith a JSON body:date(YYYY-MM-DD),time(H:MM, exactly as it came from the slot list),contactwith name, e-mail, phone prefix (+420or+421), phone, an optional note and consent to data processinggdprConsent: true. Consent is required – without it the booking won’t go through, just like in the widget. OptionallystaffIdwhen the client picks a staff member, andcustomFieldswith answers to custom fields by theirid.
No key or sign-in is needed and you can call it from any domain. The booking goes through the same rules as from the widget: opening hours, booking notice and confirmation. The customer also gets the same e-mails.
A minimal working form without libraries looks like this. Just fill in the workspace identifier from the admin:
<form id="booking">
<select name="service" required></select>
<input type="date" name="date" required>
<select name="time" required></select>
<input name="fullName" placeholder="Full name" required>
<input type="email" name="email" placeholder="E-mail" required>
<input type="tel" name="phone" placeholder="Phone" required>
<label><input type="checkbox" name="gdpr" required> I agree to the processing of my personal data</label>
<button>Book</button>
<p id="message" role="status"></p>
</form>
<script type="module">
const API = 'https://api.eazo.eu/v1'
const TENANT = 'your-identifier'
const form = document.getElementById('booking')
const fields = form.elements
const message = document.getElementById('message')
// 1. Services of your workspace
const workspace = await fetch(`${API}/tenants/${TENANT}`).then((r) => r.json())
for (const service of workspace.services) {
fields.service.add(new Option(`${service.name} (${service.durationMinutes} min)`, service.id))
}
// 2. Free times for the selected service and day
async function loadTimes() {
fields.time.length = 0
if (!fields.date.value) return
const day = await fetch(
`${API}/tenants/${TENANT}/services/${fields.service.value}/days/${fields.date.value}`,
).then((r) => r.json())
for (const time of day.available ?? []) fields.time.add(new Option(time, time))
}
fields.service.addEventListener('change', loadTimes)
fields.date.addEventListener('change', loadTimes)
// 3. Submitting the booking
form.addEventListener('submit', async (event) => {
event.preventDefault()
const response = await fetch(`${API}/tenants/${TENANT}/services/${fields.service.value}/bookings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
date: fields.date.value,
time: fields.time.value,
contact: {
name: fields.fullName.value,
email: fields.email.value,
phonePrefix: '+420',
phoneNumber: fields.phone.value,
gdprConsent: fields.gdpr.checked,
},
}),
})
const data = await response.json()
message.textContent = response.ok
? data.status === 'confirmed'
? 'Done, your booking is confirmed. Details are on their way by e-mail.'
: 'We have received your booking. We will e-mail you once it is confirmed.'
: [data.message, ...Object.values(data.fields ?? {})].join(' ')
})
</script>
Responses and errors
A successful booking returns 201 with the status confirmed (confirmed right away) or pending (waiting for your approval), plus a url link to the booking detail where the client can reschedule or cancel. An error always has the shape { "error", "message", "fields" }. The text in message and fields can be shown to the client as is; it follows the Accept-Language header (Czech or English).
400 VALIDATION_ERROR– wrong or missing data,fieldsgives the reason for each field. Missing consent is reported on thegdprConsentfield. The e-mail is also checked against DNS; the domain must accept mail.422or409 SLOT_UNAVAILABLE– someone else took the slot in the meantime. Load the free times again.429 RATE_LIMITED– too many attempts. One IP address can make 5 bookings per minute and one e-mail 5 per hour. Retrying automatically is pointless; just show the message.
Need to create, change or read bookings from your own system (server, CRM, a partner’s booking system)? That is what the partner API with a key on the Business plan is for. Don’t build a website form on it – the key would be visible in the page source.
Frequently asked questions
Will the widget slow down my website?
No. The widget loads asynchronously and separately from the rest of the page (using Shadow DOM) - your website loads first, then the widget quietly gets ready in the background.
Does it work on WordPress or Webflow?
Yes, it works anywhere you can add custom HTML - just paste the two lines from the Widget settings page into the custom code/HTML block of your CMS.
Can I have the widget on several pages with different settings?
Yes. The basic look is set in the admin and applies everywhere, but individual attributes (such as language, width or a preselected service) can be overridden for a specific embed right in the HTML code.
Can Eazo remind clients that it is time to book again?
Yes. For each service you set an interval after which Eazo sends the client an invitation to the next visit with a link straight to picking a time. It skips clients who already have another appointment booked and those who did not show up last time. The feature is part of the Pro plan.
What happens when a client cancels a slot others wanted?
With the waiting list turned on, people signed up for that day get an e-mail offering the freed slot. Whoever responds first books it – the slot is not held for anyone. The feature is part of the Pro plan.
How do I set up the widget for just one service?
Open the service in the admin and in the “Embed code (this service only)” section copy the ready-made code with the data-service attribute, plus data-lock-service if no other service should be selectable.
Didn't find an answer?
Tell us what you need to solve - we'll get back to you as soon as possible.