The clone books fine but looks wrong
The copy works. People can book it, the availability matches, and the API returned success. Then you open the booking page and it's wearing default styling instead of the colors, layout, CSS and logo the original had.
A success response only says the request was accepted. It tells you nothing about whether the copy looks like the original, and that's the part your clients see.
What the API gives back, and what it doesn't
HighLevel documents the read as GET /calendars/{calendarId}, which needs the calendars.readonly scope, and the update as PUT /calendars/{calendarId}, which needs calendars.write. The documented response does carry some presentation data: widgetType, which sets the calendar layout, along with widgetSlug and the cover image.
What we've found missing are the settings behind most of the look. In our tests these did not come back on GET:
- widgetConfig, the widget's styling
- codeBlock, the custom CSS
- widgetLogo, the logo on the booking page
- the guest and staff toggles on the booking page
Why the copy falls back to defaults
A clone built only from the GET response can't carry settings the response never had. The new calendar gets the rules it was given and defaults for everything else.
Two more things before you script this. Those fields aren't in HighLevel's documentation, so treat their names and shape as something that can change without notice, and read them fresh on every run. And the API marks meetingLocation as deprecated in favor of locationConfigurations, so copy the location from locationConfigurations.
Clone it with the look intact
This is the procedure that has held up in our own builds. Run it on one calendar and check the result by eye before you run it on many.
- Read the original with GET /calendars/{calendarId} and list the presentation settings missing from the response.
- Compare that response with what the original's public booking page actually shows, and note every difference.
- Give the copy its own booking URL. In the app this is the Custom URL, and HighLevel checks that it's available. In our tests it had to be unique within the sub-account.
- Send the missing look settings with PUT /calendars/{calendarId}, carrying the whole calendar body rather than only the fields you changed.
- Open the copy's booking page next to the original's and compare them.
- Check every website or funnel page that embeds a booking iframe. In our tests a hard-coded iframe stayed bound to the original calendar, so swap the embed wherever the copy should appear.
A success response means accepted, not identical. Open the booking page.
What trips up a clone script
Each of these can send a copy live with the wrong look, or leave a site booking into the old calendar.
- Building the clone from GET alone and calling it done
- Sending a partial body on the PUT; the full body is what worked for us
- Reusing the original's URL instead of giving the copy its own
- Hard-coding undocumented field names with no check that they're still there
- Forgetting the booking embeds on your website and funnels
Rather have it set up for you?
We'll clone your calendars with their look carried over and compare every copy's booking page to the original. Your first step is a 30-minute 1:1 with Rob.
