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 toBookingModelfor 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_meetinglead-scoring signal.
How it works
Sources
| Source | How |
|---|---|
| Widget booking flow | Visitor picks a slot inside the widget, powered by the configured calendar provider |
| Calendly | Calendly event embedded in the widget, or used externally — the invitee.created webhook still captures it |
| Google Calendar | Direct Google Calendar OAuth booking flow; widget queries slots and creates events |
| GoHighLevel Calendar | GHL calendar selection (V2 supported), booking via embed |
| Manual | Admin 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: trueshows 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
| Setting | Where | Effect |
|---|---|---|
companies.calendly_integration (event_type_uri, event_type_duration, tokens) | Integrations → Calendly | Calendly connection + default event |
companies.google_calendar_integration (access/refresh tokens) | Integrations → Google Calendar | Google connection |
companies.go_high_level.calendar_id | Integrations → GoHighLevel | Which GHL calendar to book against |
companies.go_high_level.booking_enabled | Integrations → GoHighLevel | Enable GHL widget booking |
companies.booking_button | Settings → Widget | Show the "Book a meeting" button |
lead_score.booked_meeting | Settings → Lead Scoring | Weight 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.outcomesupportsattended/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/lostspecifically 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_idif 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:
backend—GoHighLevelService(GHL calendars/booking), Calendly/Google OAuth + webhook handlers; booking write/dedup logic ontoPersonProfileModel. - Models:
PersonProfileModel.bookings[](unified per-person),BookingModel(company-wide, withoutcomeenum). - 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 withknox-history-lookupfor "what was each booked lead's full journey?". - Subsystems: CRM Integration, Identity Graph, Session Recording.
Related
- Leads — Overview — booking as a lead source
- Lead Scoring —
booked_meetingweight - Recorded Sessions — the journey behind a booking
- CRM Integration · Identity Graph