Skip to main content

Bookings — Sources & Unified View

Audience: Sales · Management · Support · QA · Engineering · Where in app: Settings → Integrations (calendar connections), Settings → Widget (booking button); bookings appear on Person Detail and the dashboard · Plan availability: All plans (a connected calendar provider is required for self-serve booking)

Bookings (a.k.a. meetings) can come from several sources — the in-widget booking flow, Calendly, Google Calendar, GoHighLevel, or a manual admin add. Whatever the source, every booking is unified onto the visitor's Person so admins and the AI see one combined timeline: the booking, the session that produced it, the lead score at booking time, and the journey before it. booked_meeting is one of the highest-weight lead-scoring signals — a booking usually pushes a visitor straight over the hot threshold.

What it does

  • Captures bookings from multiple calendar providers and the widget's own booking flow.
  • Unifies every booking onto PersonProfileModel.bookings[] and mirrors to BookingModel for fast company-wide queries.
  • Deduplicates near-identical bookings for the same person.
  • Attributes each booking to its originating session journey for Nox/reporting.
  • Sends new-booking notifications and feeds the booked_meeting lead-scoring signal.

How it works

Sources

SourceHow
Widget booking flowVisitor picks a slot inside the widget, powered by the configured calendar provider
CalendlyCalendly event embedded in the widget, or used externally — the invitee.created webhook still captures it
Google CalendarDirect Google Calendar OAuth booking flow; widget queries slots and creates events
GoHighLevel CalendarGHL calendar selection (V2 supported), booking via embed
ManualAdmin adds a booking from the dashboard (rare)

Unified storage

Every booking lands on PersonProfileModel.bookings[]:

{
source: "google" | "calendly" | "ghl" | "manual",
booking_date,
booking_time,
duration_minutes,
attendees: [{ email, name }],
user_session_id, // session that triggered it
meeting_url, // Zoom / Google Meet / etc.
status: "scheduled" | "completed" | "cancelled" | "no-show",
created_at
}

It's also persisted on BookingModel for fast company-wide queries. Dedup: same Person + similar time (within 5 minutes) collapses to one entry — keyed on (person_id, time ± 5min).

Per-source mechanics

Widget booking flow — visitor clicks "Book a meeting" (e.g. in AI chat or from a CTA) → slots fetched from the connected calendar → on selection the provider's API creates the event → confirmation to visitor + admin.

Calendly — OAuth via Settings → Integrations → Calendly; companies.calendly_integration stores tokens + event_type_uri + event_type_duration. The invitee.created webhook captures bookings even when Calendly is used outside the widget. A detected calendly.event_scheduled also counts as identity verification.

Google Calendar — OAuth via Settings → Integrations → Google Calendar; companies.google_calendar_integration stores access/refresh tokens; the widget queries slots and creates events directly.

GoHighLevel — OAuth via Settings → Integrations → GoHighLevel (UI now labels it LeadConnector); companies.go_high_level.calendar_id selects the calendar; companies.go_high_level.booking_enabled turns on widget booking. Supports GHL V2 forms, the new GHL calendar-selection UI, and embedding GHL calendars in the widget. (Handled by GoHighLevelService; see CRM Integration.)

POST /go-high-level/book-calendar also back-fills the GHL contact's phone (and mirrors an email back-fill) from what the visitor gave at booking — only when the CRM field is empty, never overwriting. Conversational-AI bookings (ms-ai book_meeting tool) send only email + user_session_id; phone/name fall back to the identity stamped on the user_session (phone_number/name, ignoring placeholder names like "Guest"), so chat-given details still land on the contact and the calendar's Phone field.

Booking buttons in the widget

  • companies.booking_button: true shows a "Book a meeting" button in the widget header.
  • The AI chat agent can offer booking via a tool call ("Would you like to book a demo?").
  • The AI call agent can book during a voice call.

Configuration & options

SettingWhereEffect
companies.calendly_integration (event_type_uri, event_type_duration, tokens)Integrations → CalendlyCalendly connection + default event
companies.google_calendar_integration (access/refresh tokens)Integrations → Google CalendarGoogle connection
companies.go_high_level.calendar_idIntegrations → GoHighLevelWhich GHL calendar to book against
companies.go_high_level.booking_enabledIntegrations → GoHighLevelEnable GHL widget booking
companies.booking_buttonSettings → WidgetShow the "Book a meeting" button
lead_score.booked_meetingSettings → Lead ScoringWeight for the booking signal (high)

Behaviors & edge cases

  • Dedup is (person_id, time ± 5min) — two genuinely different meetings booked within 5 minutes of each other for the same person can collapse into one entry.
  • Outcome tracking is not auto-populated. BookingModel.outcome supports attended / no_show / cancelled / rescheduled / closed_won / closed_lost, but nothing writes attended/no-show automatically yet — it would require calendar webhooks or CRM stage syncs. closed_won/lost specifically needs CRM stage sync.
  • External-tool bookings (Calendly used outside the widget) are captured by webhook, so they may arrive without a tied user_session_id if the person can't be matched.
  • A booking is a top-weight score signal — expect a hot-lead notification to fire around most bookings.
  • Booking confirmations and reminders come from the calendar provider (Google/Calendly/GHL), not Knock Knock.

Plan & limits

  • Bookings are supported on all plans, but self-serve widget booking requires a connected calendar provider (Google / Calendly / GHL).
  • GHL bookings additionally require the GHL integration (V2 app) connected — see CRM Integration and the v2 GHL dual-app notes.
  • Outcome write-back (attended / closed-won) is not yet available automatically.

Technical implementation

  • Service: backendGoHighLevelService (GHL calendars/booking), Calendly/Google OAuth + webhook handlers; booking write/dedup logic onto PersonProfileModel.
  • Models: PersonProfileModel.bookings[] (unified per-person), BookingModel (company-wide, with outcome enum).
  • Attribution: each booking links to its user_session_id, the full session journey (pages, conversations, friction), the lead score at booking time, and the traffic source — the basis for Nox journey questions.
  • Notifications: new booking → in-app + email (provider sends the visitor's confirmation/reminders).
  • Nox tools: knox-recent-bookings(window=24h|7d|30d), knox-visitor-bookings(person_id), combined with knox-history-lookup for "what was each booked lead's full journey?".
  • Subsystems: CRM Integration, Identity Graph, Session Recording.