Fix: GoHighLevel calendar booking stopped working
Your GHL calendar shows no slots, 500s on booking, or books the wrong time. It's almost always availability, timezone, or a round-robin with no eligible user. Here's how to tell them apart.
Symptoms — what you're seeing
- The booking widget loads but shows no available slots — even though the calendar has open hours
- Slots show up, the contact picks one, and the confirmation step 500s or spins forever
- Appointments are getting double-booked into the same slot
- The time the contact picked is offset by a few hours from what actually lands on the calendar
- A round-robin calendar accepts the booking but never assigns a user to it
- It worked last week and quietly stopped — no settings changed on your side
Why this happens
GHL calendar booking is really three systems stacked on top of each other: the availability calculation (what free slots exist), the booking write (creating the appointment against a user), and the timezone resolution that translates between the contact, the calendar, and the assigned user. A failure in any one of them looks like "booking is broken," but the fix is different for each, so the first job is telling them apart.
No slots at all is almost always an availability problem, not a bug. GHL computes open slots as calendar working hours minus the assigned user's connected-calendar busy blocks, buffers, and appointment cap. If the calendar has no eligible user, or the connected Google/Outlook calendar is fully blocked, or the "minimum scheduling notice" pushes the first slot past the visible window, the widget correctly shows nothing. On a round-robin, one user with a broken calendar connection can zero out availability for the whole team.
Slots show but booking 500s is a write-side problem. The most common cause is a round-robin calendar with no eligible user at the chosen time (everyone is at their cap or outside their hours), so the availability call returns a slot but the write has nobody to assign it to. The other frequent cause is an expired or wrong-scope integration token on whatever is doing the booking — an Edge Function, a Vapi voice agent, or an n8n workflow calling the calendar API.
Wrong-time and double-booking bugs are timezone mismatches. GHL resolves a time against the calendar's timezone, but the contact books in their timezone and the assigned user's connected calendar writes in its timezone. If those three disagree — especially across a DST boundary — you get an appointment that's technically "valid" but lands an hour off, and the slot that should have been marked busy isn't, so the next person books it too.
The fix, step by step
- Confirm the calendar has an eligible, available user. Open the calendar's settings and check that at least one team member is assigned, their working hours cover the window you're testing, and their appointment cap isn't already hit for that day. For round-robin, verify every member's connected calendar is still authenticated — a silently disconnected Google/Outlook account removes that user from availability.
- Reconcile the three timezones. They must be internally consistent, not identical — but you need to know what each one is:
Calendar timezone → Settings → Calendars → [calendar] → Timezone User timezone → each assigned user's profile timezone Contact timezone → the contact record (or the widget's detected TZ)
Use IANA identifiers (America/New_York), never fixed offsets (EST) — the offset flips on DST and that's what breaks bookings twice a year. This is the same class of bug that hits n8n schedule triggers. - Test the availability API directly, before the widget. Hit the free-slots endpoint yourself so you can see whether GHL thinks slots exist, independent of the front-end widget. If this returns slots but the widget shows none, the problem is the embed/timezone; if this returns empty, the problem is availability.
GET https://services.leadconnectorhq.com/calendars/{calendarId}/free-slots ?startDate={epochMs}&endDate={epochMs}&timezone=America/New_York Headers: Authorization: Bearer pit-xxxxxxxx-... Version: 2021-07-28A 401 here means the token — not the calendar — is the problem; jump to the 401 unauthorized fix. - Check buffer and duration math. Slot duration + before/after buffers + minimum notice all subtract from the visible window. A 60-minute slot with 15-minute buffers on each side needs a 90-minute clear block; if your working day doesn't divide cleanly, the last slot silently disappears. Widen the window or shrink the buffers and re-test.
- Verify team assignment on the write path. If availability returns slots but booking 500s, the round-robin has no assignable user at that moment. Add a fallback user, or switch the calendar to a specific user temporarily to confirm that's the cause.
- Book one test appointment end-to-end and confirm it lands at the right time on the assigned user's connected calendar — not just inside GHL. A booking that's correct in GHL but wrong in Google Calendar is a user-timezone mismatch, not an availability bug.
Common variations
- "Embedded widget shows no slots but the API returns them" — the widget is resolving the visitor's browser timezone and landing outside your working hours. Set an explicit timezone on the widget or the calendar.
- "Voice agent / Vapi booking fails but manual booking works" — the agent's integration token has calendar read scope but not write, or it's passing a timezone the calendar doesn't recognize. Check the token scopes and the timezone string the agent sends.
- "Round-robin double-books" — two members share a connected calendar, or one member's calendar connection dropped so GHL can't see their existing busy blocks. Re-authenticate every connected calendar.
- "Slots vanish after DST" — a fixed offset is stored somewhere in the chain instead of an IANA zone. Audit calendar, user, and any API caller for
UTC-5-style values and replace with region identifiers. - "Booking creates the appointment but no confirmation fires" — that's a workflow/automation problem downstream of the booking, not the calendar itself; see GHL workflow not triggering.
When this isn't the issue
- If the calendar itself is toggled off or unpublished, no amount of availability tuning will help — confirm it's active first.
- If every API call returns 401, the integration token expired or lacks calendar scope — fix the token before touching calendar settings (401 fix).
- If bookings succeed but the contact never gets a reminder, the issue is email/SMS deliverability, not booking — see GHL email deliverability.
Building a voice agent that books into GHL? The Vapi + GoHighLevel voice agent guide walks the full booking path through the availability and appointment API.
Need this workflow customized or debugged? Book a 30-min scope call →
Stuck configuring this workflow?
I build and fix n8n workflows for agencies and SaaS teams. 30-minute scope call — bring the problem, leave with an estimate.