Guides · For businesses

Online bookings for a clinic or salon, on your own server

Set up a booking system your customers use from your website: your opening hours, who they can see, what they can book, confirmation emails and reminders. It runs on a server you choose, the bookings stay in your own database, and there's no monthly fee or charge per booking.

What you need

The steps

  1. Generate the booking system

    Open the business generator and pick the Appointments (salon, clinic, studio) starting point. Fill in your business name, the owner's email address (the first person to sign in), your currency and your time zone.

    The starting point gives you a Clients record type (name, email, phone, preferences, notes) and opening hours you can change. Add record types of your own if you keep other lists, then download the zip.

    You have a folder called YourBusiness_Backend.

  2. Try it on your computer

    Double-click start-local.cmd (Windows) or start-local.command (Mac). The first time, it installs what it needs. Then open the staff portal:

    http://localhost:3000/portal

    Sign in with the owner's email and the OWNER_PASSWORD from the .env file. You're asked to choose your own password straight away; after that, delete the OWNER_PASSWORD line from .env.

    You're in the portal as the owner.

  3. Add your team and their hours

    Open Team and add the people who work with you. Each person gets a temporary password, shown once, and chooses their own when they first sign in.

    Tick Takes bookings for everyone customers can book. You can give each person their own working hours there; otherwise they follow the opening hours.

    RoleWhat they can do
    OwnerEverything, including adding or removing managers and owners.
    ManagerEverything except changing owners and other managers.
    StaffDay-to-day work: view, add and edit records and manage bookings. They can't delete (unless you allow it for a record type), export or see the activity log.
  4. Add what people can book

    Open Bookings > Services and add each service: its name, how long it takes, a gap after it for clearing up, the price shown to customers, and who can do it.

    The gap after a service is what stops back-to-back bookings with no time to change a room or clean equipment. A 60-minute massage with a 10-minute gap blocks 70 minutes of that person's day.

  5. Set your booking rules

    The rest lives in business-config.json, under "bookings". Edit it in any text editor, then restart the server. These are the values the Appointments starting point gives you:

    "bookings": {
      "enabled": true,
      "openingHours": {
        "mon": "09:00-17:30", "tue": "09:00-17:30", "wed": "09:00-17:30",
        "thu": "09:00-20:00", "fri": "09:00-17:30", "sat": "09:00-16:00", "sun": ""
      },
      "slotMinutes": 15,
      "minNoticeHours": 2,
      "maxDaysAhead": 60,
      "cancelCutoffHours": 24,
      "chooseStaff": true,
      "reminderHoursBefore": 24
    }
    SettingWhat it does
    openingHoursWhen you're open, in your time zone. "09:00-12:00, 13:00-17:00" gives a lunch break; "" means closed.
    slotMinutesHow often start times are offered: every 15 minutes, every 30, and so on.
    minNoticeHoursHow far ahead people must book. 2 means nobody can book a slot starting in the next two hours.
    maxDaysAheadHow far into the future people can book.
    cancelCutoffHoursUp to when customers can cancel with the link in their email.
    chooseStaffWhether customers can pick who they see, or just get whoever is free.
    reminderHoursBeforeWhen the reminder email goes out. 0 switches reminders off.
  6. Connect your email

    Without email settings, everything works but nothing is emailed. Add your provider's SMTP details to .env and restart:

    SMTP_HOST=smtp.yourprovider.com
    SMTP_PORT=587
    SMTP_USER=bookings@yourclinic.co.uk
    SMTP_PASS=your-smtp-password

    In business-config.json, set "email" > "from" to the sender customers see (for example "Willow Clinic <bookings@yourclinic.co.uk>") and "notify" to where new bookings are announced to you.

    Customers get a confirmation with a cancel link, and a reminder before their visit.

  7. The booking page on your website

    Try the example page first: http://localhost:3000/examples/booking.html. It walks customers through service, person, day and time, then takes their name, email and phone.

    To put booking on your own website, copy clients/YourBusinessClient.mjs into the site and use it like examples/booking.js does. Here's the whole booking flow, tested against a generated backend:

    import { WillowClinicClient } from './WillowClinicClient.mjs';
    const api = new WillowClinicClient({ url: 'https://book.willowclinic.co.uk', rememberSignIn: false });
    
    const { services } = await api.services();
    const massage = services[0];
    const { dates } = await api.dates(massage.id, { days: 14 });
    const day = dates.find((d) => d.available).date;
    const { slots } = await api.availability(massage.id, day);
    
    const { appointment } = await api.book({
      serviceId: massage.id, start: slots[0].start,
      name: 'Alex Morgan', email: 'alex@example.com', phone: '07700 900123',
    });
    console.log('Booked:', appointment.startText);  // "Monday, 9 November 2026 at 9:00"

    Then add your website's address to "allowedOrigins" in business-config.json, for example ["https://www.willowclinic.co.uk"], so browsers let your site talk to the server. Your web developer gets the full list of endpoints at /api/openapi.json.

    If your website builder can't run your own JavaScript, you can serve the example booking page from the backend itself and link to it from your site. Restyle it in examples/site.css.

  8. Go live

    Put the folder on a small server that stays on, keep it running with PM2 and put HTTPS in front with Caddy. The deploy guide goes through it on Ubuntu or Windows Server; the steps are the same for a business backend. Then:

    1. Set "publicUrl" to your address, for example "https://book.willowclinic.co.uk". The links in emails use it.
    2. If your booking page lives on your own website, set "serveExamples": false and point "manageUrl" at the page that handles the cancel link.
    3. Restart, then run the self-test on the server:
    npm run self-test

    The self-test adds a service, finds free times, books one, checks the same time can't be booked twice and tries the cancel link. Then it tidies up after itself.

Day to day

What it doesn't do

Honest limits, so there are no surprises:

Because the bookings and your clients' details sit in your own database, you're the data controller. Say what you keep and why in your website's privacy policy. The README-SETUP.txt in the download has a section on it.

Try it before you buy

Create a free account and set up the Appointments starting point: every setting works, and you see exactly which files you'd get. You only need a licence to download.

Open the business generatorBusiness backends

More for businesses: a secure staff portal with Node.js and SQLite · self-hosted vs Firebase vs Retool.