How to embed the Your Booking appointment system on your website. These instructions work with most website platforms.
More than one branch? Start with Booking Pages for Multiple Branches. Your layout — one booking page for the whole group, a button per branch, a page per branch, a shared locations page, or a branch page alongside the group one — decides which name goes in the snippets below and where your patients' appointment links land. Then come back here for the rest.
Three levels of integration. Pick whichever matches how much setup you want to do — you can always upgrade later.
Standalone: Nothing. All links go to your Your Booking subdomain (e.g. yourpractice.yourbooking.au). Your branding is applied to the booking page, but patients see our subdomain in the address bar.
Basic (booking on your site): Embed the iframe on a page of your website and set the Website Booking URL. Both email and SMS reminder links land patients on your website inside the iframe — SMS uses our short domain (e.g. yp.yrb.au) which 302-redirects to your booking page. No server configuration needed.
Full (branded SMS links): Basic plus register your own short domain (e.g. yp.au). Either point its DNS at our server (we handle the rest), or configure your domain provider's URL forwarding to your booking page. Same patient experience as Basic — only the SMS link wording changes. No .htaccess rules, no redirect rules on your web server.
Most practices start with Basic and upgrade to Full once they're happy with the booking flow.
New Zealand practices: the examples below use our Australian domains. Swap
.aufor.nzthroughout and everything else is identical — your booking address isyourpractice.yourbooking.nz, the scripts load fromhttps://yourbooking.nz/, and our default SMS short domain isyp.yrb.nz.
Create a new page on your website (e.g. "Book Online") and add the following HTML code. Most website platforms have a "Custom HTML", "Embed", or "Code" block for this.
<div id="yourbooking-embed"></div>
<script src="https://yourbooking.au/embed.js" data-tenant="yourpractice" nowprocket></script>
Replace yourpractice with your Your Booking subdomain (the part before .yourbooking.au, or .yourbooking.nz in New Zealand — load the script from the same domain your booking address uses). More than one branch? The name to use depends on your layout — see Booking Pages for Multiple Branches.
That's it. The script creates the iframe, auto-resizes it to eliminate double scrollbars, and reads tokens from the URL so that links from emails and SMS open the correct view inside the iframe (appointment management, recall booking, or feedback). Your Booking maintains the script centrally — when the link format changes, you don't have to update anything on your site.
If your website theme adds padding around the content area, you may also need to add before the embed:
<style>.your-content-container { padding: 0 !important; }</style>
Replace .your-content-container with the appropriate CSS selector for your theme.
If your site has a strict Content Security Policy that blocks third-party scripts, or you prefer to vendor the integration yourself, you can use a self-contained version that doesn't depend on yourbooking.au/embed.js:
<iframe
id="yourbooking"
src="https://yourpractice.yourbooking.au/?embed"
style="width: 100%; min-height: 700px; border: none; overflow: hidden;"
scrolling="no"
allow="payment; geolocation"
title="Book an Appointment">
</iframe>
<!-- nowprocket keeps WP Rocket from delaying this script; do not remove -->
<script nowprocket>
(function() {
var iframe = document.getElementById('yourbooking');
// Subdomain is read from the iframe src above — set it there once;
// nothing else in this script needs editing.
var base = new URL(iframe.src).origin;
var params = new URLSearchParams(window.location.search);
var t = params.get('t');
var appt = params.get('appt');
if (!t && !appt) {
// If this page's own address ends in a booking token, use it. The
// shape is deliberately narrow so an ordinary page name — /bankstown,
// /book-online — is never mistaken for a token.
var segs = window.location.pathname.split('/').filter(Boolean);
var last = segs[segs.length - 1];
if (last && /^([2-9][23456789A-HJ-NP-Za-km-z]{5})$/.test(last)) {
t = last;
}
}
if (t) {
iframe.src = base + '/t/' + encodeURIComponent(t) + '?embed';
} else if (appt) {
iframe.src = base + '/appointment/' + encodeURIComponent(appt) + '?embed';
}
// Token links open a specific view (manage/recall/feedback) — bring the
// widget into view in case the embed sits below the fold on a long page.
if (t || appt) {
iframe.scrollIntoView({ block: 'start' });
}
window.addEventListener('message', function(e) {
if (!e.data || typeof e.data !== 'object') return;
if (e.data.type === 'resize' && typeof e.data.height === 'number') {
iframe.style.height = e.data.height + 'px';
}
if (e.data.type === 'scroll-to-top') {
iframe.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
})();
</script>
About the allow attribute. payment lets the booking page collect a deposit where a practice takes one. geolocation lets the branch list's Sort by nearest location button, and ?branch=closest links, work for multi-branch groups. Neither causes a prompt by itself: the attribute only permits the booking page to ask, and it asks only when a patient taps that button. A single-branch practice can leave geolocation in place — the button never appears when there is nothing to choose between. If your snippet predates this and lacks it, patients still get the ordinary branch picker.
Replace yourpractice with your subdomain in the iframe src — that's the only place it needs changing. The script reads the subdomain back from the iframe, so appointment-management and recall links from SMS/email automatically point at the right place. If the SMS or email link format changes in future, you'll need to update this snippet by hand — the hosted script above updates automatically.
Performance or caching plugins (WP Rocket, LiteSpeed, etc.). These often have a "Delay JavaScript Execution" option that holds scripts back until the visitor first clicks or scrolls. If the resize script is delayed, the booking page can't report its height, so it shows collapsed or clipped on phones until the visitor interacts.
The nowprocket attribute on the script tag above prevents this on WP Rocket — leave it in place. After adding the snippet (or editing it), clear the WP Rocket cache (WP Rocket → Clear Cache) so the change goes live. If your site uses LiteSpeed Cache instead, add data-no-defer="1" to the same tag — <script nowprocket data-no-defer="1"> — then purge the LiteSpeed cache. For other plugins (Autoptimize, Perfmatters, etc.), exclude the script from JavaScript delay/defer using their exclusion field; getElementById('yourbooking') is a unique string to match. The min-height: 700px is a safety net so the page stays usable even if a delay slips through.
Prefer a "Book Online" button that opens the booking form in a pop-up instead of an inline page? Add a trigger button and one script line:
<button type="button" data-yb-open>Book Now</button>
<script src="https://yourbooking.au/ybpopover.js" data-tenant="yourpractice" async></script>
Replace yourpractice with your Your Booking subdomain. Any element marked data-yb-open opens the pop-over — use as many as you like on a page. Escape, the backdrop or the × closes it.
The script brings its own overlay and styling, so it needs no modal framework and works anywhere you can paste HTML. The booking form is loaded when someone opens the pop-over, not on page load, so a visitor who never books never waits for it.
Appointment links work in the pop-over. A manage, recall or feedback link arriving from SMS or email carries a token in the page URL; the pop-over opens by itself and shows that appointment rather than a fresh booking.
Set the Website Booking URL (Step 2) to the page carrying the button — just the page address, with nothing added to the end:
| Website Booking URL | https://www.mypractice.com.au/book-online |
| Link we send | https://www.mypractice.com.au/book-online?t=2kR9mQ |
We add the ?t=... part when we send the link, and the pop-over opens on seeing it. There is nothing to configure beyond the page address.
The page doesn't have to be a booking page. If your "Book Now" button sits in your site header, the setting can be your home page — https://www.mypractice.com.au — and the pop-over opens over whichever page the patient lands on, with their appointment already in it.
Your Booking maintains this script centrally — when the link format changes, you don't have to update anything on your site.
Live availability label (optional). The button can also show your soonest availability — e.g. "Book Now · available tomorrow" — by adding the data-yb-button attribute and a second script line:
<button type="button" data-yb-open data-yb-button data-label="Book Now" data-template="{label} · {text}">Book Now</button>
<script src="https://yourbooking.au/ybpopover.js" data-tenant="yourpractice" async></script>
<script src="https://yourbooking.au/ybbutton.js" data-tenant="yourpractice" async></script>
Turn on Enable website button under Setup > Email & Domains > New-Patient Availability. Until then — or whenever your practice agent is offline or has no open slots — it silently shows a plain "Book Now" and the pop-over still works. Tune the wording with data-template.
Your phone number in the pop-over (recommended). The pop-over covers your page, so your own footer — and the phone number in it — is out of reach while the booking form is open. Turn on Show our contact footer when embedded under Setup > Email & Domains > Domain Settings and the booking form carries your phone number, email and website address along its foot. Leave it off for an inline embed, where your page footer is still on screen below the form.
WordPress/WP Rocket caveat. If the button doesn't open, a caching plugin delayed the script — add nowprocket to the <script> tags (WP Rocket) or data-no-defer="1" (LiteSpeed), then clear the cache.
If you'd rather not load a script from us, or you want to restyle the modal, this version pastes the whole thing onto your page. It behaves the same as the hosted script above, including appointment links. The trade-off is that it's a fixed copy: improvements we make later reach the hosted script automatically, but this one only when you paste it again.
<!-- Your Booking — "Book Now" pop-over -->
<!-- (A) Trigger button. It can also show live availability
(data-yb-button) — see the note above. -->
<button type="button" data-ybsh-open>Book Now</button>
<!-- (B) Paste this block once, just before </body> -->
<div id="ybsh-overlay" class="ybsh-overlay" role="dialog" aria-modal="true" aria-label="Book an appointment" hidden>
<div class="ybsh-modal">
<button type="button" class="ybsh-close" data-ybsh-close aria-label="Close">×</button>
<iframe class="ybsh-frame" title="Book an appointment" allow="payment; geolocation"></iframe>
</div>
</div>
<style>
.ybsh-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;z-index:2147483000;padding:16px}
.ybsh-overlay[hidden]{display:none}
.ybsh-modal{position:relative;width:100%;max-width:480px;height:90vh;max-height:900px;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 12px 48px rgba(0,0,0,.35)}
.ybsh-frame{width:100%;height:100%;border:0;display:block}
.ybsh-close{position:absolute;top:8px;right:8px;width:36px;height:36px;padding:0;border:0;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;font:700 22px/1 sans-serif;cursor:pointer;z-index:1}
.ybsh-close:hover{background:rgba(0,0,0,.78)}
@media(max-width:520px){.ybsh-overlay{padding:0}.ybsh-modal{height:100%;max-height:none;border-radius:0}}
</style>
<script>
(function(){
var BASE='https://yourpractice.yourbooking.au';
var overlay=document.getElementById('ybsh-overlay');
if(overlay.parentNode!==document.body){document.body.appendChild(overlay);} // escape page-builder CSS transforms (Elementor, Divi, etc.)
var frame=overlay.querySelector('.ybsh-frame');
// Carry an appointment token from the page URL, same as our hosted scripts.
var q=new URLSearchParams(location.search),t=q.get('t'),appt=q.get('appt');
if(!t&&!appt){var segs=location.pathname.split('/').filter(Boolean),last=segs[segs.length-1];
if(last&&/^([2-9][23456789A-HJ-NP-Za-km-z]{5})$/.test(last))t=last;}
var SRC=t?BASE+'/t/'+encodeURIComponent(t)+'?embed'
:appt?BASE+'/appointment/'+encodeURIComponent(appt)+'?embed'
:BASE+'/?embed';
function open(){frame.src=SRC;overlay.hidden=false;document.body.style.overflow='hidden';overlay.querySelector('.ybsh-close').focus();}
function close(){overlay.hidden=true;document.body.style.overflow='';frame.src='about:blank';}
document.addEventListener('click',function(e){
if(e.target.closest('[data-ybsh-open]')){e.preventDefault();open();}
else if(e.target.closest('[data-ybsh-close]')||e.target===overlay){close();}
});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&!overlay.hidden)close();});
if(t||appt)open(); // arrived from an SMS/email appointment link
})();
</script>
Replace yourpractice in BASE with your subdomain — .yourbooking.nz in New Zealand. The block moves its own modal to the end of <body> on load, so it isn't clipped or mispositioned when your "Book Now" button lives inside a page-builder container — Elementor, Divi, and similar apply CSS transforms that otherwise break a fixed overlay.
Wix needs its own approach: Wix runs embedded HTML/iframes inside its own sandboxed frame, so the hosted embed.js auto-resize signal can't reach the top page — the embed stays whatever height you set. Pick one of two ways.
No-code (fixed height). Quickest; the form sits in a fixed-size box.
https://yourpractice.yourbooking.au/?embedBecause Wix can't auto-resize the box, a taller step may show an inner scrollbar — make the box taller and republish if so.
Auto-resize (Wix Dev Mode / Velo). Removes the inner scrollbar by letting Wix code set the height. Turn on Dev Mode (Velo), then use an Embed HTML element (give it the ID bookingHtml) whose markup relays the booking page's height up to Velo:
<iframe id="yb" src="https://yourpractice.yourbooking.au/?embed"
style="width:100%;border:none;" scrolling="no"
allow="payment; geolocation"></iframe>
<script>
// The booking page posts its height to THIS embed's document; relay it
// up to the Wix page so Velo (below) can resize the element. The same
// relay forwards booking events (see "Track bookings in your own
// analytics" below) — without it they stop here and never reach Wix.
window.addEventListener('message', function (e) {
if (!e.data || typeof e.data.type !== 'string') return;
if (e.data.type === 'resize' || e.data.type.indexOf('yourbooking:') === 0) {
window.parent.postMessage(e.data, '*');
}
});
</script>
Then in the page's Velo code:
$w.onReady(() => {
$w('#bookingHtml').onMessage((e) => {
if (e.data?.type === 'resize' && typeof e.data.height === 'number') {
$w('#bookingHtml').height = e.data.height;
}
});
});
The inner iframe reports its height → the relay forwards it up → Velo sets the element height. True auto-resize, no inner scrollbar.
Note on SMS/email links. Wix's sandbox makes the token-routing in the self-contained snippet above impractical, so on Wix the embedded page just hosts the start of booking. Appointment-management and recall links from SMS/email open the booking subdomain (or your short domain) directly, rather than routing into the Wix page — so this doesn't need handling on the Wix side.
If you've turned on Web Chat (Admin → Settings → Patient Chat), a chat button appears on your booking page automatically — nothing to install. To put the same chat button on every page of your website, not just the booking page, paste this one-liner into your site's HTML <head>:
<script src="https://yourbooking.au/ybchat.js" data-tenant="yourpractice" async></script>
Replace yourpractice with your Your Booking subdomain. Then turn on Embed script installed on website in the Patient Chat settings — this tells the booking-page widget to step aside so visitors see a single chat button (the site-wide one), with no double-up. Conversations started anywhere on your site land in the same staff dashboard Chat tab. See the Patient Chat guide for the full walkthrough.
When a patient finishes booking, the booking page tells the page it's embedded in. Your own Google Tag Manager, Google Analytics or advertising tags can listen for that and record the booking as a conversion — on your site, in the visitor's existing session, with whatever brought them there still attached.
That last part is the reason this exists. The booking form runs in a frame on your page, and a tag loaded inside that frame can't see the information identifying an ad click or a search visit, because that arrives on your page. Tracking the booking from your own page keeps the two joined up.
There's nothing to switch on. The message is sent on every completed booking, and does nothing at all unless your site listens for it.
{
type: 'yourbooking:booking-complete',
practice: 'yourpractice', // which practice was booked
new_patient: true // first booking at that practice
}
It carries no patient information and no appointment details — just the fact that a booking happened, and where. Booking a specific appointment type says something about a person's health, so that is never included.
Create a Custom HTML tag set to fire on Initialisation — All Pages:
<script>
window.addEventListener('message', function (e) {
if (!/\.yourbooking\.(au|nz)$/.test(e.origin)) return;
if (!e.data || e.data.type !== 'yourbooking:booking-complete') return;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'yb_booking_complete',
yb_practice: e.data.practice,
yb_new_patient: e.data.new_patient
});
});
</script>
Then add a Custom Event trigger matching the event name yb_booking_complete, and point whichever tags should count a booking at it — a GA4 event tag, a Google Ads conversion, a Meta conversion, or all three. yb_practice is available as a Data Layer Variable if you run more than one practice from the same site template and want them reported separately.
If you use Google Analytics directly, the same listener can call gtag instead of pushing to the data layer:
<script>
window.addEventListener('message', function (e) {
if (!/\.yourbooking\.(au|nz)$/.test(e.origin)) return;
if (!e.data || e.data.type !== 'yourbooking:booking-complete') return;
gtag('event', 'book_appointment', { practice: e.data.practice });
});
</script>
Mark book_appointment as a key event in Google Analytics to have it counted as a conversion.
If you run a Meta Pixel, the same listener fires Meta's standard Schedule event. Your base pixel code must already be on the page — add this after it:
<script>
window.addEventListener('message', function (e) {
if (!/\.yourbooking\.(au|nz)$/.test(e.origin)) return;
if (!e.data || e.data.type !== 'yourbooking:booking-complete') return;
fbq('track', 'Schedule');
});
</script>
Attach the event to the message, not to a button click. The booking happens inside the booking page, so a Schedule event fired when someone clicks “Book Now” counts everyone who opened the form and never finished. The numbers look plausible, which is what makes it hard to spot.
Send no parameters with it. Schedule on its own records that an appointment was booked. Adding the appointment type, the practitioner, or anything describing the visit sends health information to an advertising platform, which is a breach of Meta's terms and can get your domain restricted. The message deliberately carries no such fields, and nothing else should be added by hand.
Open your booking page with Tag Manager's Preview mode running and complete a test booking. yb_booking_complete should appear in the event list the moment the confirmation screen shows. Cancel the test appointment afterwards.
If nothing arrives and your booking page is embedded inside another embed — the Wix setup above is the common case — check the relay snippet forwards yourbooking: messages, or the event stops one level down.
Testing on a staging or local copy of your site won't work either. The listener deliberately ignores anything that didn't come from a yourbooking.au (or .nz) address, so a booking page pointed at a test environment is rejected on purpose. Test against the live booking address.
This only applies to a booking page embedded in your own website. If patients book at your yourbooking.au address directly, there is no surrounding page to receive the message and no conversion is recorded. Embedding the booking page in your own site is what makes conversion tracking possible.
In the admin dashboard under Setup > Email & Domains, set:
Website Booking URL — the full URL of the page carrying the booking page or the "Book Now" button, e.g. https://www.mypractice.com.au/book-online (no trailing slash). Enter the page address only; we add the token as a query parameter — …/book-online?t=2kR9mQ — when we send the link.
Once set, appointment links in booking confirmation, reminder and recall messages point at your website with the token added as a query parameter. The embed opens it in the iframe; the pop-over opens itself.
Set this only if your booking page can read the token. The standard embed, the self-contained alternative and the pop-over (Step 1) all read the token from the URL and open appointment-management and recall links on your own page. Wix is the exception — its sandbox can't route tokens. If you're on Wix, or aren't embedding the booking page at all, leave the Website Booking URL blank so those links open the Your Booking–hosted page directly and keep working. Setting it otherwise sends patients to a page that can't open their appointment.
This is all you need for the Basic (inline embed) integration.
If you want SMS reminder and recall links to show your own short domain (e.g. yp.au) instead of our default (e.g. lv.yrb.au), register a short .au domain — a .nz domain in New Zealand, where our default is lv.yrb.nz — and pick one of two options.
Easiest setup. You register the short domain, point a DNS A record at our server, and we handle everything else: SSL certificate, redirect to your booking page, updates when your booking URL changes.
Hosting the redirect is an annual charge, billed once a year and shown on your Billing tab. See Billing.
No .htaccess rules. No URL forwarding configuration. No redirect maintenance on your end. SSL certificate is issued and renewed automatically.
Your short domain doubles as a short alias for your main website. Any request to https://yp.au/<path> forwards to the same path on your main site — https://yp.au/forms/intake lands on https://www.mypractice.com.au/forms/intake. Useful on flyers, posters, intake forms, or anywhere a memorable short URL helps.
If the DNS check fails, it's usually a propagation delay. Wait a bit and click Check DNS again. Multi-branch businesses can't use this optional self-service — contact us and we'll set it up at the business level.
If you prefer to manage the redirect yourself (e.g. through your domain registrar's URL forwarding feature or Cloudflare), you can do that instead. There's no charge from us for this option. Note: we can't troubleshoot URL forwarders for you — the steps differ by provider and we don't have access to your account.
https://yp.au/ → https://www.mypractice.com.au/book-online. The forwarder must preserve the path so the appointment token reaches your booking page. Most providers call this "forward path" or "preserve path".Test by visiting https://yp.au/btest1234 in a browser before enabling SMS reminders. It should land on your booking page.
| Link type | Example URL | Opens |
|---|---|---|
| SMS manage | yp.au/2kR9mQ | Appointment management |
| SMS recall | yp.au/3xY9kQ | Recall booking flow |
| SMS feedback | yp.au/4pW2nH | Feedback page |
| Email manage | www.mypractice.com.au/book-online?appt=… | Appointment management |
When the booking page is embedded in an iframe, "Add to Home Screen" uses your website's metadata. Without this step, patients get a generic icon. With it, they get a branded app icon and a full-screen experience.
1. Upload a manifest file to your website at /booking-manifest.json:
{
"name": "Your Practice Name",
"short_name": "Booking",
"display": "standalone",
"start_url": "/book",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{
"src": "https://yourpractice.yourbooking.au/api/v1/tenant/icon",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "https://yourpractice.yourbooking.au/api/v1/tenant/icon",
"sizes": "512x512",
"type": "image/png"
}
]
}
background_color — splash screen colour while launching. Usually white, or your dark-theme background.
theme_color — tints the status bar on Android. Use your brand colour.
2. Add these tags to the <head> of the booking page:
<link rel="manifest" href="/booking-manifest.json">
<link rel="apple-touch-icon" href="https://yourpractice.yourbooking.au/api/v1/tenant/icon">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Your Practice Name">
Replace practice names and subdomains as appropriate. For best results, upload a square logo in the admin dashboard under Branding > Square icon.
| Issue | Fix |
|---|---|
| Email link opens yourbooking.au instead of your website | Set Website Booking URL in admin Domain Settings |
| Iframe doesn't load | Check data-tenant matches your Your Booking subdomain (or, for the self-contained version, that both src and base URLs match) |
| Double scrollbars | The iframe resize script may not be installed — check Step 1 |
| White border around widget | Your theme adds padding — add CSS override before the iframe |
| Short domain doesn't redirect (Option A) | Check DNS is pointing at our server (may take a few minutes to propagate); check SMS Short Domain is set in admin |
| Short domain doesn't redirect (Option B) | Check your URL forwarding preserves the path; contact your domain provider if unsure |