For the complete documentation index, see llms.txt. This page is also available as Markdown.

Appointment Self-Booking Portal

This books against one calendar

Before you start

Learning focus points

Objective

Stop booking appointments by email.

A customer asks for a call. Someone answers with three times. Two are gone by the time the customer reads the message, so they propose two more, and one of those collides with something that was booked an hour ago. Four messages later there is a meeting, and everybody has spent a day on a decision that takes fifteen seconds.

The information that would end this exists already. It is in the calendar. The customer simply cannot see it, and the only person who can is the one writing the emails.


Solution

A page the customer opens themselves, showing the free time and nothing else. They pick a slot, leave their name, and the appointment is in the calendar before they close the tab.

The free time is not a list anybody maintains. The flow reads what is already booked, turns each booking into an interval the picker treats as dead, and offers everything that is left.

How It Works (User Perspective)

  1. The customer opens the booking page. No login, no account.

  2. They see a week of half hour slots. The ones already taken are visibly there but cannot be clicked, so the page reads as a calendar rather than as a form that rejects them.

  3. They pick a free slot and give a name and an email address.

  4. The appointment is written to the calendar and confirmed on screen.

A week of half hour time slots from Sunday 13 to Saturday 19 September, most in blue, with eleven greyed out across Monday to Friday
The booking screen at run time. Every greyed slot is an appointment that already exists on the calendar the flow read, and no two of them were typed in by hand. Captured in the demo org rather than on a live site, so this is the screen itself and not the page around it.

What we'll create

  • 1 screen flow carrying the whole journey

  • 1 query of what is already booked

  • 1 loop turning those bookings into disabled intervals

  • 1 Avonni Date Time Picker offering what is left

  • 1 record create writing the appointment

  • 1 public page on an Experience site to run it on


Set up

Prerequisites

A calendar to read and write

  • Standard Event records work, and so does a custom object. Whatever you use needs a start, an end and something to filter on, so the flow reads one person's bookings rather than the whole org's.

  • Decide that filter before you build. It is the difference between a portal that offers a consultant's free time and one that offers the free time of everybody in the company at once.

An Experience site with a public page

  • A site with guest access, and a page that requires no login.

  • The guest user profile needs read access to the calendar object and create access to write the appointment. Grant exactly those two and nothing else.

The Avonni Flow Screen Components package

  • The Date Time Picker used here is a flow screen component. There is no Experience Builder component that does this on its own, which is why the whole journey is a flow rather than a page of components.

Build the flow

1

Create the screen flow

Create a screen flow and name it for the thing it does, for example Appointment_Booking. Everything on this page lives inside it, including the parts a visitor never sees.

2

Read what is already booked

Add a Get Records on your calendar object. Filter it to the calendar this page books against, and sort by start time.

Keep this query narrow. A booking page that reads six months of history to draw one week is slow for no reason, and on a public page it is slow for everyone at once.

3

Create the collection the picker will read

The picker does not accept a list of records. It accepts a collection of intervals, each one a start and an end.

Create two variables of the Apex-Defined type DateTimePickerDisabledInterval, which ships with the package: one single value, and one collection. The single one is scratch space inside the loop, the collection is what the screen will read.

4

Turn each booking into an interval

Add a Loop over the records from the query, and inside it one Assignment with three lines: set the scratch variable's startDate from the record's start, set its endDate from the record's end, then add the scratch variable to the collection.

That is the whole translation. Nothing else in the flow has to know what a booking is.

A flow canvas: Start, Get booked time, an Each booking loop containing Add busy slot, then screens Pick a time and Your details, then Book the slot and Appointment confirmed
The whole flow. The query and the loop exist for one reason, which is to have the intervals ready before the screen is ever shown.

Build the booking screen

1

Add the Date Time Picker

Add a screen named Pick a time and put an Avonni Date Time Picker on it, on its own.

Set Variant to Weekly so the customer compares days instead of paging through them one at a time, and Type to Radio so they leave with one slot rather than several.

2

Set the working window

Under Visible Dates and Times, set Start Time and End Time to the hours you actually take appointments, and Time Slot Duration to the length of one appointment. Every slot on the page is generated from these three values.

Set Start Date and End Date to the window you are willing to be booked in, and Default Visible Date to a day inside it.

Do not skip that last one. If the picker opens on a week that falls entirely outside the start and end dates, it does not show an empty week, it fails with a component error.

3

Attach the busy intervals

Set Disabled Intervals to the collection the loop built.

This is the step that gets missed. Disabled Intervals looks exactly like the three fields above it, which are lists you fill in by hand, and it is the only one here holding a reference. Everything on this page depends on it being a reference: fill it in by hand and you have published a calendar that was accurate on the day you built it.

Turn on Show Disabled Dates so the taken slots stay visible. A booked slot that disappears looks like a slot that never existed, and the customer wonders why Tuesday morning is missing.

The Visible Dates and Times section of the property panel, with Start and End dates, start and end times, three empty Add Item lists, and Disabled Intervals holding a reference named Busy Slots
Disabled Intervals holds the collection the loop built. The three lists above it are empty on purpose: those are the manual ones.

Write the appointment

1

Collect who is booking

Add a second screen asking for a name and an email address. Ask for as little as you can defend. Every extra field on a public booking page is a customer who books by email instead.

2

Create the record

Add a Create Records on your calendar object. The start comes from the picker's value, the length is the slot duration you set, and the subject is worth building from the visitor's name so the calendar entry reads as something rather than as Appointment.

Set the same field the Get Records filters on, or the booking you just wrote will not be treated as busy the next time somebody opens the page.

3

Confirm on screen

Finish on a screen that says the appointment exists. A Scoped Notification in its success variant is enough, and it costs one component.

Put it on the site

1

Add the flow to a public page

In Experience Builder, drop the standard Flow component on a page that does not require login, and point it at your flow.

2

Check it as a stranger

Open the page in a private window, signed out. This is the only test that means anything: a guest user missing one permission fails differently from an internal user, and it usually fails silently by showing an empty week rather than an error.


Going further

Close the gap between picking and booking. Two people can open the page at the same time and pick the same slot, because the intervals were built when each page loaded. Before the Create Records, add a second Get Records on that exact slot and route to an apology screen if anything comes back. It costs one decision and it is the difference between a booking page and a booking page you can leave running.

Book across a team. The pattern extends by changing what the first query returns. Read several calendars, build the intervals from all of them, and a slot is offered only when everybody is free. Round robin is the same query with a different rule about who gets written into the record.

Leave room between appointments. Widen each interval as you build it, rather than trying to express a gap in the picker. Fifteen minutes on each side of a booking is fifteen minutes nobody can book against.

Let people change their mind. A cancel or reschedule link is the same flow with the record in hand, and it removes most of the email that this page was built to end.

Related components

  • Date Time Picker

  • Scoped Notification

Was this helpful?