Booking on your website (embed)
With the embed, customers pick the service and the time and confirm the
booking without leaving your website. Paste one line of code on the page:
the embed.js script mounts booking in a safe iframe served by
https://calendar.sendeasy.app.
The fastest way is to generate the code in the dashboard: Calendar → Public page → Share. Pick the calendar, the service, the format, the color and the campaign, copy the ready code and check the preview.
Requirements
- Website booking enabled for your company.
- A time slot calendar with active event types (services). Lodging calendars have no embed: use the calendar link.
- Your website on https. Pages on
httpor opened as a local file cannot show booking.
Installation
Paste the code where booking should show:
<script src="https://calendar.sendeasy.app/embed.js"
data-sendeasy-calendar="YOUR_CALENDAR"
data-mode="inline"
async></script>
YOUR_CALENDAR is the calendar address, the same as in the link
https://calendar.sendeasy.app/c/YOUR_CALENDAR.
To place booking inside an element of yours (instead of right after the tag), mark the element and load the script once on the page:
<div data-sendeasy-calendar="YOUR_CALENDAR" data-type="consultation-30"></div>
<script src="https://calendar.sendeasy.app/embed.js" async></script>
Formats
data-mode | What shows |
|---|---|
inline (default) | Booking inside the page, right after the tag (or inside the marked element). The height follows the content. |
popup | A button where the tag is. Clicking it opens booking in a window over the page. On a marked element that is not a <script>, the element itself becomes the button. |
button | A floating button fixed in the bottom right corner of the page, which opens the same window. |
The window closes on its X, with Esc, with a click outside it or with the Close button of the confirmation.
Options
- Name
data-sendeasy-calendar- Type
- required
- Description
Calendar address (letters, digits,
.,_,~and-, up to 190 characters).
- Name
data-type- Type
- optional
- Description
Service (event type) address, for example
consultation-30. Opens that service directly, hidden services included. Without it, the customer sees the list of services.
- Name
data-mode- Type
- optional
- Description
inline,popuporbutton. Any other value becomesinline.
- Name
data-label- Type
- optional
- Description
Button text for the
popupandbuttonformats, up to 60 characters. Default:Agendar.
- Name
data-color- Type
- optional
- Description
Color of the button and of the booking highlights, in hexadecimal (
#0f766eor#0a7).
- Name
data-lang- Type
- optional
- Description
Booking language:
pt,pt-BR,en,en-US,esores-ES. Without it, the visitor's browser language applies.
- Name
data-utm-source, data-utm-medium, data-utm-campaign, data-utm-term, data-utm-content- Type
- optional
- Description
Booking campaign, up to 200 characters each. The booking is already marked as coming from the website (with the domain, never the page path); UTM tags help tell pages or actions apart.
Values out of format are ignored: booking opens with the default. No value becomes HTML: the button text goes in as plain text.
JavaScript API
With embed.js on the page, open booking from any button of yours:
<button type="button" id="book">Book now</button>
<script src="https://calendar.sendeasy.app/embed.js" async></script>
<script>
document.getElementById('book').addEventListener('click', function () {
if (window.SendeasyCalendar) {
window.SendeasyCalendar.open({ calendar: 'YOUR_CALENDAR', type: 'consultation-30' })
}
})
</script>
window.SendeasyCalendar.open(options)opens the window and returnstrue(orfalsewith an invalid calendar). Options:calendar(required),type,color,langandutm({ source, medium, campaign, term, content }).window.SendeasyCalendar.close()closes the open window.
Since the script loads with async, check that window.SendeasyCalendar exists
before calling it.
Messages to your website (postMessage v1)
Booking notifies the page with postMessage. Every message has this shape:
{ "source": "sendeasy", "v": 1, "type": "..." }
type | Fields | When |
|---|---|---|
ready | Booking has loaded. | |
resize | height (px, up to 20000) | The content height changed. embed.js already adjusts the inline format by itself. |
slot_selected | date (YYYY-MM-DD), time (HH:mm) | The customer picked a time. Local date and time of the business. |
booking.created | code, status, startAt | The booking was created. code is the 8 character code (or null), status is HOLD, PENDING_PAYMENT, AWAITING_APPROVAL, CONFIRMED or CANCELLED, and startAt is the start in ISO 8601 (UTC). |
booking.payment_required | code | The booking needs payment; the payment button opens the checkout in a new tab. |
closed | The customer closed booking (Esc or Close), in the popup and button formats. |
Messages carry no personal data: no name, email, phone, answers, payment or management links. Booking accepts no messages from your page. Always check the origin before using them:
window.addEventListener('message', function (event) {
if (event.origin !== 'https://calendar.sendeasy.app') return
var data = event.data
if (!data || data.source !== 'sendeasy' || data.v !== 1) return
if (data.type === 'booking.created') {
// Example: conversion event in Google Analytics 4
gtag('event', 'booking_created', { status: data.status })
}
})
Messages are only sent to https pages whose origin the browser gives to the
iframe. Unknown origin: no message is sent.
Your website CSP
If your website sends a Content-Security-Policy header, allow the calendar
domain for the script and for the iframe:
Content-Security-Policy: script-src 'self' https://calendar.sendeasy.app; frame-src https://calendar.sendeasy.app
Add these origins to the directives you already use (script-src and
frame-src, or child-src if the site does not use frame-src). On the
Sendeasy side, the iframe can be shown by any https website.
Pinned version with SRI (optional)
/embed.js gets fixes automatically (cached for up to one day). To pin a
version with Subresource Integrity, read the version and the hash from the
response headers:
curl -sI https://calendar.sendeasy.app/embed.js | grep -i x-sendeasy-embed
and use both values:
<script src="https://calendar.sendeasy.app/embed.js?v=VERSION"
integrity="sha384-HASH"
crossorigin="anonymous"
data-sendeasy-calendar="YOUR_CALENDAR"
async></script>
When the script changes, the old version no longer matches the hash and the browser runs nothing: update both values.
Limits
- Payment always opens in the top level tab, in a new tab, outside your
website. The checkout and the booking management links can never be shown
inside an
iframe. httpswebsites only.httppages do not show booking.- Time slot calendars with event types only. Lodging keeps using the calendar link.
- The
iframekeeps nothing between visits (browsers block third party storage); the confirmation stays on screen until the customer closes it. - The booking page inside the
iframeis not indexed by search engines. To show up on Google, use your Public page and the Google Business Profile. - One window at a time: opening another closes the previous one.
Common problems
| Symptom | What to check |
|---|---|
| Nothing shows | Is the feature enabled for the company? Is the calendar address right? Does the browser console show a CSP block? |
| Error page inside the frame | The calendar has no active services, or the data-type service does not exist. |
Booking does not show on http | The website must be on https. |
| Payment opens in another tab | That is expected: the checkout never runs inside your website. |