How to build a Vapi voice agent that books into GoHighLevel

    A voice assistant that qualifies callers, checks real GHL calendar availability, and writes the booking back to your CRM — with the n8n backend and the timezone and latency gotchas that make or break a live call.

    Last updated 2026-09-03~7 min read
    VapiGoHighLeveln8nCalendar APIVoice Agent

    Skip the backend build. Download the n8n workflow JSON — two webhook endpoints (check_availability and book_appointment) pre-wired to the GHL free-slots and appointment APIs, timezone passthrough, contact upsert, and immediate 200 responses. Point your Vapi tools at them.

    What you're building

    A phone call comes in (or the agent dials out). A Vapi voice assistant qualifies the caller, checks real availability against a GoHighLevel calendar, offers open times, and books the appointment — writing the contact and the booking back into GHL so the CRM stays the single source of truth. Nothing lives in a spreadsheet; the same calendar your team already works out of gets the appointment.

    The hard part isn't the voice — Vapi handles speech. The hard part is the booking backend: turning "does Thursday at 2 work?" into a real availability check and a real write to GHL, fast enough that the caller isn't sitting in silence. That backend is an n8n workflow (or a Supabase Edge Function) that Vapi calls as a tool.

    Prerequisites

    • A Vapi account with a phone number and an assistant
    • A GoHighLevel sub-account with a calendar and at least one assignable user
    • A GHL Private Integration token scoped to calendars.readonly, calendars/events.write, contacts.write
    • n8n reachable on a public URL (Vapi tools call it over HTTPS)

    Step 1: Build the Vapi assistant and qualification prompt

    In Vapi, create an assistant with a system prompt that does one job at a time: greet, qualify against your criteria (budget, timeline, fit), then move to booking. Keep it linear — voice agents wander when the prompt lists everything at once.

    You are a booking assistant for [company]. Qualify the caller on
    [criteria]. If they qualify, offer to book a 30-minute call. Ask for
    their timezone before offering times. Call the check_availability tool
    to get real slots — never invent times. After they pick one, call the
    book_appointment tool. Confirm the date, time, and timezone back to
    them before ending.

    Step 2: Add a check_availability tool

    Define a Vapi function/tool called check_availability that POSTs to your n8n webhook with a date range and the caller's timezone. The n8n workflow calls GHL's free-slots endpoint and returns open times as a short list Vapi can read aloud.

    GET https://services.leadconnectorhq.com/calendars/{calendarId}/free-slots
      ?startDate={epochMs}&endDate={epochMs}&timezone={callerTz}
    Headers:
      Authorization: Bearer pit-xxxx...
      Version: 2021-07-28

    Return only the next 3–4 slots. A voice agent reading twelve options is a hang-up. If the endpoint returns nothing, that's usually an availability or timezone problem, not a Vapi problem — see GHL calendar booking failures.

    Step 3: Add a book_appointment tool

    Define a second tool, book_appointment, that POSTs the chosen slot, the caller's name, phone, and timezone to a second n8n webhook. That workflow upserts the contact and writes the appointment to GHL, returning a confirmation Vapi reads back.

    • Upsert the contact first (create or match on phone/email) so you have a contact ID
    • Write the appointment against the calendar and assigned user with the caller's timezone
    • Return the confirmed local time — Vapi confirms it out loud before hanging up

    Step 4: Keep the CRM as source of truth

    Every call should leave a trace in GHL whether or not it books: create/update the contact, tag the call outcome, and move the pipeline stage. Don't let qualified callers exist only in Vapi's call logs — write them into GHL so your existing follow-up automations pick them up. This is the same contact-write discipline as the webhook pipeline.

    Step 5: Reliability on a live call

    • Answer fast. Return the availability list well under a couple of seconds — the caller is on the line. Cache the calendar's working hours if the free-slots call is slow.
    • Respond 200 immediately, then write. On the booking webhook, acknowledge before doing slow downstream work, so Vapi isn't waiting on your Slack notification.
    • Dedupe repeat calls. A caller who redials, or a Vapi retry, shouldn't create a second appointment. Key the write on phone + slot and drop repeats — the pattern in the reliability checklist.

    Common gotchas

    1. Timezone drift. The caller's timezone, the calendar's timezone, and the assigned user's timezone must all reconcile, or the confirmed time lands an hour off. Ask the caller for their timezone up front and pass it to both tools. Use IANA identifiers, never fixed offsets.

    2. Latency budget. Every tool call adds silence to the call. Keep the n8n workflows lean — one GHL call each, no unnecessary enrichment mid-conversation. Do the heavy work after the 200.

    3. DTMF and no-input fallbacks. Some callers won't speak clearly. Give the assistant a fallback path (repeat the options, offer to text a booking link) so a misheard slot doesn't dead-end the call.

    4. Token scope. A token that can read the calendar but not write appointments books nothing and errors silently mid-call. Grant calendars/events.write and test the booking tool in isolation before going live.

    Browse 4,900+ ready-to-import workflow templates →

    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.

    Related pages