# Publish booking pages and embed them

> Publish a Coach's booking pages, share their links, embed them on your own website with the iframe and height script, use pay-only and manage links, optionally approve each booking and each move yourself, and ask people to accept your Terms and Conditions and Privacy Policy.

## Goal

Let people book a session with you without signing in: publish one or more **booking pages**, share their links, or put them on your own website as an iframe. Each page has its own address, hours, approval rule and Offers, so a free discovery call and a paid session can each have the page that suits them. A person picks an Offer and a slot; for a priced Offer the booking hands the client straight to Stripe Checkout.

The examples on this page use a sample Coach whose slug is `jane-doe` and an Offer with the id `42`. Replace `jane-doe` with your slug and `42` with your Offer's id everywhere they appear, including inside the snippets.

## Prerequisites

- An onboarded Coach account.
- At least one Offer of your own (**Booking**, **Offers**; see [Connect Stripe and request payments](https://coaching-toolkit.com/docs/payments/stripe-connect)). A page takes bookings only when it shows an Offer. An Offer holds one or more kinds of session, each with a length and a count (say 1 × intake, 30 minutes, then 5 × session, 60 minutes); a booking is for the Offer's first session and lasts as long as that one.

## 1. Set up your profile and your pages

Your **public profile**, set once in **Booking**, **Public profile**, is what every page shows above the booking form: your address (your slug), the name on the page, headline, about text, photo and the timezone of your hours.

Your **booking pages** are in the app under **Booking**, **Booking pages**. Each page has a title, an intro, its own address after yours, weekly hours, slot step, buffer, notice and days ahead, a booking approval rule and the Offers it shows. Your first page is your **default** page: the one your plain link, your clients' portal and your landing page's booking button open; you can make any other page the default instead. Switch on **Take bookings (publish the page)** and save.

| Setting | Rule |
| --- | --- |
| Your address (profile slug) | 3 to 40 lower-case letters, digits and single hyphens; unique; changeable once every 90 days |
| Page address (page slug) | the same form; unique among your pages (a colleague may use the same one); free to change while the page is off, at most once every 90 days while it takes bookings |
| Title | up to 120 characters |
| Intro | optional, up to 2,000 characters |
| Take bookings (publish the page) | on publishes the page and needs at least one Offer; off takes it offline |
| Timezone | on your profile; an IANA timezone (for example `Europe/London`); the hours of every page are in it |
| Weekly hours | up to 28 windows, each a weekday (Monday to Sunday) with a start and end time as `HH:MM` |
| Slot step | 15, 30 or 60 minutes: how often a slot starts |
| Buffer | 0 to 120 minutes between sessions |
| Notice | 0 to 336 hours |
| Days ahead | 1 to 90 |
| Offers to show | the page owner's active Offers (an Admin on a colleague's page picks from the colleague's), in the order you pick; a booking lasts as long as the Offer's first session |
| Booking approval | **Book instantly** (a booking is confirmed at once, the default) or **Approve each booking** (you approve each booking and each move, see below) |

Reserved slugs for your address: `app`, `api`, `admin`, `portal`, `book`, `pay`, `docs`, `www`, `mail`, `support`, `billing`, `coaching-toolkit`, `truetovalues`, `manage`. A page address can be none of those, nor `slots`, `pay`, `manage`, `pages` or `embed`. The app tells you while you type whether a slug is available.

Open slots come from the page's weekly hours minus your planned sessions and, if you connected a calendar, its busy time. Your sessions from every page block the same time.

### How many pages

How many booking pages each Coach can have depends on the practice's package: Free 1, Starter 3, Growth 5, Studio 10. Every page counts, whether it takes bookings or not; delete one to make room. After a move to a smaller package nothing is deleted: your default page and then your oldest pages stay active, and the pages past the limit are marked inactive. You can still edit or delete them, but they answer as a page that takes no bookings until the practice upgrades or you delete another page.

## 2. Share the link

The link to your default page:

```text
https://coaching-toolkit.com/book/jane-doe
```

Any other page has its page address after yours, for example `https://coaching-toolkit.com/book/jane-doe/discovery-call` for a page whose address is `discovery-call`.

With an Offer of the page preselected, add `?offer=` and its id:

```text
https://coaching-toolkit.com/book/jane-doe?offer=42
```

## 3. Embed a page on your website

Paste this where the booking page should appear (the example is the default page). It is the iframe followed by a small script that grows the iframe to the page's height, so there is no inner scroll bar:

```html
<iframe id="coaching-toolkit-booking-jane-doe" src="https://coaching-toolkit.com/book/jane-doe?embed=1" title="Book a session with Jane Doe" style="width:100%;height:640px;border:0;background:transparent" loading="lazy" allow="payment"></iframe>
<script>(function(){var f=document.getElementById("coaching-toolkit-booking-jane-doe");window.addEventListener("message",function(e){if(!f||e.origin!=="https://coaching-toolkit.com"||e.source!==f.contentWindow||!e.data||e.data.type!=="coaching-toolkit:height")return;var h=Number(e.data.height);if(h>0&&h<20000)f.style.height=Math.ceil(h)+"px";});})();</script>
```

- `embed=1` hides the page's own header and makes its background transparent, so it sits on your site's colours.
- The script accepts height messages only from the booking page's origin and only from that iframe.
- `allow="payment"` lets Stripe Checkout open from the frame.
- Keep the `id` and the script's `getElementById` the same if you rename one.

If your page already has the iframe, the height script alone is:

```html
<script>(function(){var f=document.getElementById("coaching-toolkit-booking-jane-doe");window.addEventListener("message",function(e){if(!f||e.origin!=="https://coaching-toolkit.com"||e.source!==f.contentWindow||!e.data||e.data.type!=="coaching-toolkit:height")return;var h=Number(e.data.height);if(h>0&&h<20000)f.style.height=Math.ceil(h)+"px";});})();</script>
```

Each page has its own snippets, with your slug and the page filled in, on that page in **Booking**, **Booking pages** (**Share and embed**, **Embed on your website**, **Copy**). Two pages on one website get iframes with different ids. The snippets also list one pay-only link per priced Offer on the page. Prefer copying from there.

## 4. Pay-only links

A pay-only link skips the slot picker and goes straight to Checkout for one of your priced Offers paid online; arrange the session separately. It needs a connected Stripe account. An Offer paid by other means has no pay-only link. A pay link sells one of your priced Offers as long as one of your pages takes bookings.

```text
https://coaching-toolkit.com/pay/jane-doe/42
```

## What happens on a booking

1. The client picks an Offer and a slot, gives their name and email (and optionally phone and a note), and confirms.
2. Coaching Toolkit finds the client in your practice by email, or creates a client profile assigned to you.
3. The client buys the Offer: a purchase with all its sessions (see [Sessions a client has left](https://coaching-toolkit.com/docs/clients/create-and-portal#sessions-a-client-has-left)). A Planned session is created with the length and kind of the Offer's first session, counting toward that purchase, keeping the Offer and the page it was booked on, and, if you connected a calendar, added to it. The rest of the Offer's sessions are booked later from the portal, or you plan them.
4. For a priced Offer, a Pending payment is created and the client goes to Stripe Checkout. If the client does not pay, the session stays Planned; you decide what to do. For an Offer paid by other means there is no Checkout and no Stripe account is needed: the confirmation, the mail and the manage page show your payment instructions, and you mark the payment as paid once the money is in.
5. The client gets a confirmation mail with a **manage link**; you get a "New booking" mail. A new client also gets a link to their portal.

The manage link (`/book/manage/{token}`) works until seven days after the session ends, and follows the session when it is moved: the client can cancel or move the booking while it is Planned and has not started, withdraw a request you have not decided yet, and see what is owed with a link to pay. A cancellation withdraws an open payment request (and releases a card hold); refunding a paid one is your decision in Stripe. A client who cancels a booked session keeps the purchase, and the session can be booked again from it.

## Bookings from the client portal

Your existing clients can also book from their portal (**Sessions**, **Book a session**; see [Create a client and send a portal link](https://coaching-toolkit.com/docs/clients/create-and-portal)). The portal shows the default page of the client's assigned Coach and uses the same open slots, Offers, Booking approval, payment hand-off and mails as that page; only the contact form is skipped, since the client is known. Taking the default page offline also removes **Book a session** from the portal.

In the portal a client cancels until 24 hours before the start (and can withdraw a request still waiting for you, which the manage link cannot), and moves a session with the manage link's rules. A client with sessions left in a purchase books the next one from it instead of buying the Offer again: no new payment.

## Approve each booking (optional)

By default a booking is confirmed at once. If you want to look at each one first, set **Booking approval** to **Approve each booking** on that page (**Booking**, **Booking pages**); each page has its own rule. Switching back to **Book instantly** changes nothing for requests already waiting.

### What the client sees

1. The page says that you confirm each booking, and the button reads **Send request**.
2. After sending, the page says **Request received**. The session holds its time on your page, so nobody else can take the slot, but it is not booked yet: no meeting link, no calendar entry, no reminder.
3. The client's mail says the request was received and links to the manage page, which shows "waiting for your coach".
4. When you approve, the client gets a confirmation with the meeting link and "add to calendar" links. When you decline, they get your message (if you wrote one) and a link to **Pick another time**, which sends a new request from the same manage page.

### What you see and do

- A mail asking you to approve or decline, with a link to your agenda, and an alert on the client's card (Warning, Urgent after 48 hours).
- The agenda shows the request under **Awaiting your approval** at the top, with **Approve** and **Decline** (an optional message to the client, at most 300 characters). The same buttons are on the client card's sessions and alert strip.
- An approved request becomes a Planned session and is added to your connected calendar. It counts as planned in the session tally only from then on; in the purchase it holds its place from the request.
- A request you leave for 7 days, or whose time passes, is declined automatically; you and the client both get a mail.
- A request you decline, one that expires and one the client withdraws give back the purchase the booking created (it is cancelled), unless another session already counts toward it.

### Payment hold

For a priced Offer with Stripe connected, the client's card is **authorised, not charged**, when they send the request (the payment shows as `Authorised`). Approving charges it (`Paid`). Declining, or the 7-day expiry, cancels the hold (`Released`): nothing is charged and there is no refund or fee. Card holds last about 7 days, which is why requests expire after 7 days. If the charge fails when you approve, the session stays approved and the client card shows an Urgent alert so you can ask for payment another way. Free Offers skip all of this.

### Reschedule requests

With approval on, a client who moves a booking through the manage link **proposes** a new time instead of moving it ("Propose a new time"); they can add a short note. Their session keeps its time until you decide, and the proposed time is held on your page meanwhile.

- You get a mail and an alert; the agenda and the client card show **Reschedule requested:** and the proposed time, with **Approve new time** and **Keep current time** (with an optional message).
- Approving moves the session, updates your calendar and sends the client a new confirmation. Declining keeps the session where it is and tells the client, who may propose again.
- A session can have one proposal waiting at a time and three in all. A proposal you leave for 7 days, or whose time passes, expires and the session stays as it is.
- When you move a session yourself, it moves at once, as without approval.

## Consents: Terms and Conditions and Privacy Policy (optional)

Your practice can ask every person to accept a **Terms and Conditions** text, a **Privacy Policy** text, either or both, before they book. An Admin sets them in **Booking**, **Booking terms**: paste the text, **Save**; **Remove** takes it off. Each text has at most 20,000 characters. They apply to the booking pages of every Coach of the practice.

### What the person sees

- Each text in full, above the **Book** button, in its own box that scrolls when it is long, followed by a required tick: "I accept the Terms and Conditions". The button stays off until every box is ticked. The same goes for pay-only links and for the page inside your website.
- In the client portal, the texts appear in the booking step only until the client has accepted the current version of each.
- Rescheduling, cancelling and picking another time after a declined request ask nothing: the person already accepted for that booking.

### Writing the texts

The texts are Markdown, with a small subset kept: headings (`#`, `##`, `###`; deeper ones show as `###`), paragraphs, bullet lists (`-`), numbered lists (`1.`), **bold** (`**bold**`), *italic* (`*italic*`) and links to `https://`, `http://` and `mailto:` addresses. A list inside a list shows as one flat list. Everything else is shown as plain text (code, quotes, tables) or left out (images, horizontal rules). HTML is stripped, and a link to any other kind of address shows as its text. A numbered list pasted from Word or Google Docs usually comes through as a numbered list.

### What is recorded

Every accepted text becomes a **consent record** on the client: which text, its version, when, where (booking page or portal), the booking, and the IP address and browser of the request. The client card's Overview lists them ("Accepted Terms and Conditions (version 3f1c0e9a7b2d) on ..."), and the client record export includes them. Records are deleted with the client when you erase them.

A text's **version** is derived from the text itself: any edit is a new version, and everyone is asked to accept it again, portal clients included. Saving the same text again changes nothing.

### Consent errors

- If a text was not ticked, the page asks for it and keeps the booking from going through.
- If a ticked text changed meanwhile (edited or removed), the page shows the current texts again with the ticks cleared, and the person ticks them again.


## Bot protection

The booking form has a hidden honeypot field and, when this deployment has Cloudflare Turnstile keys, a Turnstile check. You do nothing for either; they work inside the iframe on your website as well.

## Verify it worked

Open **Booking**, **Booking pages**, a page, **Share and embed**: it shows the page's link and snippets with your slug filled in. Open the page link in a private browser window: your name and headline, the page's title and its Offers show, and your open slots are listed.

## Failure cases

What can stop you when you save the page:

- **The slug is not accepted.** It must be 3 to 40 lower-case letters, digits and hyphens and must not be a reserved word; your address must not belong to another Coach, and a page address must not be another of your pages'.
- **You cannot change the slug yet.** You changed it in the last 90 days (for a page: while it was taking bookings).
- **An Offer cannot be shown.** A page can only show your own active Offers.
- **The page needs an Offer.** A page takes bookings only when it shows at least one Offer.
- **You have as many booking pages as your package allows.** Delete a page or upgrade the package.
- **The hours do not make sense.** A window ends before it starts.
- **The embed snippets are missing.** Save the page first.
- **You cannot approve or decline a session.** It is not a request waiting for you (it was decided already or expired), or its time has passed.
- **You cannot decide a reschedule proposal.** It was decided already or expired.

What a person booking can run into:

- **The page is not published.** Booking is switched off, the page is past your package's limit, or none of its Offers can be booked (each archived or past the limit). The page still opens, without Offers or slots.
- **The page does not exist.** The address names no page of yours.
- **The Offer is not on the page.** No Offer was picked, or it is not one the page can sell; also shown for a pay-only link to a free Offer or one paid by other means.
- **The slot was taken.** Someone else took it, or your calendar became busy.
- **The coach is not taking clients.** A booking from the portal by a client whose engagement has ended.
- **Payments are unavailable.** A pay-only link while Stripe is not connected or cannot charge, or when the package has no client payments.
- **The bot check failed.**
- **The booking cannot be changed.** On the manage link: the session can no longer be cancelled or moved, or a request is still waiting for you.
- **A proposed new time is already waiting for you.** On the manage link, a client can have one proposal open at a time.
- **The session has had its three proposals.** No further reschedule proposals are accepted.
- **A Terms and Conditions or Privacy Policy text was not ticked.**
- **A text changed while the page was open.** The person reads and ticks the new text again.
