Add to your website
Paste a script or an iframe on your site to show a booking page without sending people away.
This is not the Embed section further down a booking page's settings. That one is a Pro feature that puts your own HTML on the confirmation page.
Script and iframe
- Open a booking page from Booking pages.
- Scroll to Add to your website. It is the second section on the page.
- Turn off the fields your website will not pass, then copy a snippet.
Both snippets are free on every plan.
Script snippet (recommended)
Auto-resizes to fit the page as the guest moves through the booking flow.
<div data-callslot-booking data-url="https://app.callslot.app/book/YOUR-PAGE" data-name="" data-email="" style="min-height:640px"></div>
<script src="https://app.callslot.app/embed/booking.js" async></script>The script mounts the page on a data-callslot-booking element.
data-name and data-email are empty placeholders. Fill them in, or turn those fields off in the app to drop them from the snippet.
Plain iframe
No JavaScript, but the height is fixed and does not resize.
<iframe src="https://app.callslot.app/book/YOUR-PAGE?name=&email=&embed=1" title="Book a time" style="width:100%;border:0;min-height:640px;"></iframe>The iframe loads your public page with ?embed=1, which hides CallSlot chrome. The script snippet adds this parameter for you.
The empty name and email values are placeholders too. Fill them in, or turn those fields off in the app.
The embedded page reports its height to the script with postMessage, and the script resizes the iframe to match. Guests get one scrollbar on your page and none inside the widget.
The plain iframe cannot do this. Pick a min-height that fits your longest step, or use the script instead.
Any site can frame a published booking page. There is no list of allowed domains, unlike the calendar embed.
Script options
The container div reads these attributes.
- data-url — required. The booking page URL on your CallSlot domain. The loader refuses any URL on a different origin than the script itself.
- data-min-height — the starting height in pixels, and a floor the widget grows past. Default 640. A value that is not a number is ignored, with a console warning.
- data-title — the iframe title announced by screen readers. Defaults to Book a time.
- data-name, data-email, data-phone, data-company and data-q-<id> — prefill values. See Pass guest details.
- data-a1 … data-a10 — prefill by position instead of by id. data-q-<id> is safer. See Pass guest details.
The loader also dispatches events on the container div, useful for analytics:
- callslot:ready — the widget has mounted.
- callslot:resize — the widget's height changed. event.detail.height holds the new height in pixels.
- callslot:booked — a guest completed a booking. It carries no personal data.
All three events bubble, so one listener on document catches every widget on the page.
A small JS API helps single-page apps:
- window.CallSlotBooking.refresh() — re-scans the page after your framework swaps the DOM. Pass an element to scan only that part of the page.
- window.CallSlotBooking.mount(element) — mounts one container.
Pass guest details
Prefill the booking form so a known guest has less to fill in. There are three ways to pass details. If more than one is present, the one higher in this list wins:
- Query params already on the iframe src or the data-url attribute.
- data- attributes on the container: data-name, data-email, data-phone, data-company, data-q-<id>, and data-a1 … data-a10.
- The same keys on the host page's own URL, forwarded automatically — like UTMs.
Only these keys are forwarded. No other query params from the host page are passed through.
The keys:
- name — guest name.
- email — guest email.
- phone — the reserved phone question.
- company — the reserved business-name question.
- q_<id> — a custom question, by its stable id. This is the form the app writes for you, and the one to use.
- a1 … a10 — custom questions by position. The count runs over the visible custom questions of the event type the guest picked, and phone and business name are not counted. On a page with more than one event type the same value lands on a different question, so use q_<id> there.
In Add to your website, every field starts on. Turn off the ones your site will not pass, then fill the empty values in the snippet. The snippets update as you go.
When the name and email are valid and every required question has a valid answer, the guest sees a short review of the details instead of the form, with Edit details and Confirm booking. Nothing is ever sent for them.
Tracking and branding
The Meta pixel never fires inside an embed. Listen for the callslot:booked event on your own page instead. It carries no personal data.
document.addEventListener("callslot:booked", () => {
// Your own analytics call goes here.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ event: "callslot_booked" });
});On the free plan, a Powered by CallSlot line shows inside the widget. Pro can turn it off — see Plans and billing.
The widget follows the visitor's light or dark setting on their device, not your site's. There is no theme or colour attribute. The one colour control is the booking page's own brand colour.
WordPress, Webflow and Framer
- WordPress — Add a Custom HTML block, not a paragraph block, and paste both lines. In the classic editor, switch to the Text tab first. Some security plugins strip script tags — if the widget never appears, use the iframe snippet instead.
- Webflow — Drop an Embed element where the widget should sit and paste both lines. Publish before testing — embeds do not run in the Designer canvas. Set the element to full width so the widget can size itself.
- Framer — Insert an Embed component, choose HTML, and paste both lines. Set the component's width to Fill and its height to Fit so the resize messages take effect. Preview or publish to test — the canvas does not run the script.
Troubleshooting
When the script cannot show the widget, it leaves a plain Book a time link in its place, not a blank box. Four things cause it:
- data-url points at another domain. It must be the domain the script came from. A short link or a custom domain that only redirects is refused.
- data-url is not a valid URL.
- The browser blocked the frame. Your own Content Security Policy must allow this origin in frame-src for both snippets, and in script-src for the script snippet.
- The framed page did not report itself ready within 6 seconds.
Check the browser console. The loader logs a warning that names the cause.
- The booking page must be published, and embedding must be turned on for this CallSlot instance. If Add to your website is missing from the page's settings, it is turned off.
- Cookies are not the problem. The framed booking flow uses no cookies and no local storage, so tracking prevention does not break it.
- Embedded bookings skip the CAPTCHA. In exchange they share a limit of 5 bookings per hour per network address, on top of the usual 10 per hour per page per address.
- Browsers cache the loader for 5 minutes. Behind a CDN, an old copy can be served for up to a day.
- The receipt and the manage page open inside the frame, so the guest never leaves your site.