Workspace369 · Booking

Booking embed examples

Every supported way to put the scheduler on a website, running live on this page. Copy a block, swap the workspace id and page slug for your own — Share & embed in the app copies both — and it works.

These embeds book against a demo workspace called Apex Auto Spa. Anything you book here is test data; book freely.

01

Inline on the page

The scheduler renders where the snippet sits and grows to fit its own content. Reach for this on a dedicated "Book" page, at the foot of a service page, or anywhere the visitor arrived already intending to book.

The live scheduler below is the Apex Auto Spa default page: three services, instant booking.

The frame carries its own credit; the embed adds the host-page one — both are expected.

HTML
<script src="https://369.dev/booking.js" data-booking
  data-workspace="demo_showcase_apex_auto_spa" async></script>

The inline snippet renders where you paste it, so it has to sit in the body — a script in <head> has nowhere to render. This examples page loads booking.js in <head>, so it uses W369Booking.mount() instead (see "Inside your own modal" below).

03

Your own button or link

Keep your own button, link or navigation item and let it open the scheduler. Add data-w369-booking to any element; the click is intercepted wherever it is on the page, including elements added later.

The link below is a real trigger: it opens the "choose your specialist" page in the overlay.

HTML
<script src="https://369.dev/booking.js" data-booking
  data-workspace="demo_showcase_apex_auto_spa"
  data-page="choose-specialist"
  data-mode="api" async></script>

<a href="https://369.dev/ws/demo_showcase_apex_auto_spa/book/choose-specialist" data-w369-booking>
  Choose your specialist
</a>

data-mode="api" installs the overlay and the JavaScript API without our floating button, so your trigger is the only one on the page. Keep a real href on the link: before the script loads (and for anyone without JavaScript) it still goes to the booking page. The trigger can override the page, service or member with its own data-page, data-service and data-member attributes.

04

Your own form, then the scheduler

Collect the details in your own form — with your own fields, validation and analytics — then hand them to the scheduler, which opens straight on the time picker. The visitor never types their name twice.

Fill the form below and it opens the scheduler at the time step with your answers already in it.

HTML
<script src="https://369.dev/booking.js" data-booking
  data-workspace="demo_showcase_apex_auto_spa"
  data-service="demo_booking_express_wash"
  data-mode="api" async></script>

<form id="lead-form">
  <input name="name" placeholder="Your name" required>
  <input name="email" type="email" placeholder="you@company.com" required>
  <!-- Phone numbers must be E.164: +, country code, number -->
  <input name="phone" type="tel" placeholder="+14155550100" required>
  <input name="company" placeholder="Company">
  <button type="submit">Pick a time</button>
</form>

<script>
  document.getElementById('lead-form').addEventListener('submit', function (event) {
    event.preventDefault();
    var answers = new FormData(event.target);
    W369Booking.open({
      step: 'time',
      layout: 'minimal',
      prefill: {
        name: answers.get('name'),
        email: answers.get('email'),
        phone: answers.get('phone'),
        metadata: { company: answers.get('company'), source: 'examples' }
      }
    });
  });
</script>

layout: 'minimal' drops the page chrome your form already replaced — the stepper, the "Book appointment" heading, the service gallery and the pane headings — leaving the time picker and a confirm step. data-mode="api" gives you W369Booking.open() and the overlay with no floating button of ours — your form is the trigger. metadata is yours to shape: up to 20 keys, 500 characters each, stored on the booking and shown on the client record. If booking.js is still loading when someone submits, wrap the open() call in the w369Ready helper from the modal example below.

05

Inside your own modal

Render the scheduler inside an element you own — a dialog, a drawer, a tab, a step in your own checkout. W369Booking.mount() puts the frame where you point it and hands back a handle; destroy() takes it away again.

The button below opens a real <dialog> with the intake-questions page mounted inside it, prefilled with a vehicle and two priorities.

HTML
<script src="https://369.dev/booking.js" data-booking
  data-workspace="demo_showcase_apex_auto_spa"
  data-page="intake-forms" data-mode="api" async></script>

<button type="button" id="open-modal">Book a detail</button>

<dialog id="booking-modal">
  <button type="button" id="close-modal" aria-label="Close">Close</button>
  <div id="modal-slot"></div>
</dialog>

<script>
  // booking.js loads async, so wait for it before calling mount().
  function w369Ready(fn) {
    if (window.W369Booking && typeof window.W369Booking.mount === 'function') fn(window.W369Booking);
    else window.addEventListener('w369:ready', function (e) { fn(e.detail.api); }, { once: true });
  }

  var modal = document.getElementById('booking-modal');
  var handle = null;

  document.getElementById('open-modal').addEventListener('click', function () {
    modal.showModal();
    w369Ready(function (api) {
      handle = api.mount('#modal-slot', {
        step: 'time',
        layout: 'minimal',
        accent: '#111827',
        prefill: {
          name: 'Ada Lovelace',
          email: 'ada@example.com',
          phone: '+14155550100',
          intake: {
            vehicle_year_make_model: '2022 Tesla Model 3',
            detail_priorities: ['Odor removal', 'Leather conditioning']
          }
        }
      });
    });
  });

  document.getElementById('close-modal').addEventListener('click', function () { modal.close(); });

  modal.addEventListener('close', function () {
    if (handle) handle.destroy();
    handle = null;
  });
</script>

layout: 'minimal' removes the page chrome a dialog has no room for — the stepper, the heading, the service gallery, the pane headings — so the modal opens on the time picker and moves to a confirm step; accent tints the primary button, the selected day and the selected time to match your own palette. data-mode="api" keeps our floating button off the page — your dialog is the only booking surface. The frame re-measures itself when its container becomes visible, so mounting into a closed dialog is fine — it sizes itself the moment you open it. Always destroy() when the modal closes: it removes the frame and its listeners. Mounting into the same element twice destroys the previous frame first, so re-opening never stacks them. Intake answers are keyed by question id; Share & embed in the app lists them under IDs for developers.

06

Plain iframe

No script at all — one iframe at a fixed height. Use it where a site builder or CMS blocks custom JavaScript. You lose auto-sizing and the booked event, so pick a height that fits the whole flow.

Request approval: the booking waits for the team. The frame below is the deposit-review page, where a request holds its slot until someone accepts it.

HTML
<iframe src="https://369.dev/ws/demo_showcase_apex_auto_spa/book/deposit-review?embed=1" title="Book an appointment" width="100%" height="900" style="border:0;display:block" loading="lazy"></iframe>
<a href="https://workspace369.com/?utm_source=embed&amp;utm_medium=iframe-snippet&amp;utm_campaign=powered-by&amp;utm_content=iframe" target="_blank" rel="noopener" style="display:inline-flex;align-items:center;gap:6px;margin-top:8px;font:500 12px/1 system-ui,sans-serif;color:#6b7280;text-decoration:none"><img src="https://369.dev/logo/favicon-32x32.png" width="14" height="14" alt=""> Powered by <strong>Workspace369</strong></a>

This is exactly what the app copies under Share & embed → Iframe. The credit anchor is part of the snippet because there is no script to add it.

07

Layouts and themes

The booking page itself decides the layout, the service images and the buffers; the embed decides the colours. Set data-theme on the script tag to override the page's own daisyUI theme without touching the page.

HTML
<script src="https://369.dev/booking.js" data-booking
  data-workspace="demo_showcase_apex_auto_spa"
  data-page="large-gallery"
  data-theme="nord" async></script>

data-theme takes any of the 35 daisyUI theme names; six representative ones are linked above. Leave it off and the page uses the theme set in the page editor.

Phone numbers

Every phone you prefill — and every phone a visitor types — has to be E.164: a +, the country calling code, then the number. +14155550100, +442071234567 and +48221234567 are all valid. The embed passes anything else through for the visitor to correct in the scheduler, and the server refuses a booking it cannot parse.

JavaScript
W369Booking.open({ prefill: { phone: '+14155550100' } });

Workspace ID and page slug for your own embeds live under Share & embed on any booking page in the app.

Book a detail