Skip to main content

GHL Integration

Audience: Agency-success / Support / Engineering · Where in app: /agency/ghl-instructions, dashboard "Connect GHL" card, /agency/companies/create-ghl · Plan availability: Agency tier; connect gated by agency.manage_settings, create gated by agency.add_client_subscription

This page covers the agency-scope GoHighLevel integration: connecting the agency's GHL agency account, creating Knock Knock client accounts mapped to GHL sub-accounts, and how the platform chooses between the v1 and v2 marketplace apps. For the cross-app mechanics (token refresh, webhooks, identity), see the CRM integration subsystem and the GoHighLevel integration module.

What it does

  • Connect an agency's GoHighLevel agency account to Knock Knock via OAuth.
  • Create client accounts from GHL — provision a Knock Knock account per GHL sub-account and get an install URL to map them.
  • Disconnect / uninstall the GHL app at agency scope.
  • Selects the correct marketplace app version (v1/v2) for new installs/reconnects and stamps it on the stored token.

How it works

Connecting the agency

From the dashboard "Connect with your GHL Agency Account" card (visible with agency.manage_settings) or the GHL Instructions screen:

  1. installGhl()POST /go-high-level/install-url/agency returns a GHL OAuth URL.
  2. The user is redirected to GoHighLevel and must link the agency main account (not a sub-account), choosing "Install under all locations" and "Enable automatic installations" (both pre-checked in GhlInstallModal).
  3. GHL redirects back to /ghlinstallation/install; the callback exchanges the code, then bounces to the agency dashboard with integration=go-high-level&success=true.
  4. Tokens land in agency.go_high_level (access_token, refresh_token, access_token_expires_at, user_type, company_id, custom_menu_id, app_version).

Connected state is read from agency.go_high_level.access_token.

Creating a client from GHL

With GHL connected, /agency/companies/create-ghl (createCompanyForGHL, POST /agencies/companies/ghl) provisions a Knock Knock company + web_owner user + default chatbot + a client subscription on the chosen package, decrements available_sub_accounts, and returns a ghl_install_url. The agency follows that URL to install Knock Knock into the matching GHL sub-account. One Knock Knock account per GHL client. See Managing Companies for the full form/fields.

v1 / v2 app selection

A stored GHL token is bound to the marketplace app that minted it, so refresh must use that same app's credentials (refreshing a v1 token with v2 creds yields invalid_grant). The platform handles this:

  • New installs / reconnects use the effective install version: a per-agency super-admin override (agency.go_high_level_install_version, 'v1'/'v2') wins; otherwise the global goHighLevel.new_install_app_version config (defaults v1).
  • GoHighLevelService.getInstallUrl resolves credentials via _appCreds(version) and carries the chosen app_version through OAuth state so the callback exchanges + stamps with the same app.
  • Existing tokens always refresh against the app_version stored on go_high_level (or ghl_self_account) — the install version override does not touch already-connected tokens.

This dual-app migration (scaffolding, webhooks, reconnect) is in progress; getInstallUrl always targets v2 for new installs once configured, while refresh respects each stored token's app version.

Configuration & options

Connect / disconnect actions

ActionRepository fnEndpointGate
Get agency install URLinstallGhl()POST /go-high-level/install-url/agencyagency.manage_settings (UI card)
Uninstall agencyuninstallGhl(agencyId)POST /go-high-level/uninstall/agencyconfirm dialog
Install for a companyinstallGhlForCompany(companyId)POST /go-high-level/install-url
Uninstall from a companyuninstallGhlFromCompany(companyId)POST /go-high-level/uninstall

agency.go_high_level fields (AgencyModel)

FieldNotes
access_token, refresh_token, access_token_expires_atOAuth tokens
user_type, user_id, company_idGHL identifiers
custom_menu_idGHL custom menu link
app_versionWhich marketplace app minted the token (v1 default / v2) — refresh uses this

Install-version override (AgencyModel)

FieldNotes
go_high_level_install_versionSuper-admin override for which app new installs/reconnects under this agency use; null → global default. Does not touch live tokens. Guarded to 'v1'/'v2' by installAppVersionForAgency

Install modal copy (GhlInstallModal.vue)

Instructs: link the GHL Agency main account (not a sub-account); both "Install under all locations" and "Enable automatic installations" are enforced (checked + disabled).

What syncs at agency scope

  • Connection / tokens at the agency level enable creating and mapping GHL client sub-accounts.
  • Per-client data sync (contacts, conversations, opportunities, webhooks) happens at company scope once Knock Knock is installed into a sub-account — that machinery is documented in the CRM integration subsystem and GoHighLevel module, not here.
  • loadAgencyAdminDataOnGHL (GET /agencies/load-data-on-ghl/load) supports loading agency admin data inside the GHL embedded context.

Behaviors & edge cases

  • Agency account, not sub-account. The modal and instructions are explicit: connect the GHL agency main account. Linking a sub-account here is a misconfiguration.
  • Token/app pairing is load-bearing. Never refresh a token with the wrong app's creds — the platform reads app_version off the stored token specifically to avoid invalid_grant.
  • Override ≠ migration. Setting go_high_level_install_version only changes the app used for the next install/reconnect; already-connected agencies/companies keep refreshing against their stored version until they reconnect.
  • Capacity still applies. Creating a GHL client consumes a sub-account slot and is blocked at available_sub_accounts < 1.
  • Disconnect is a confirm action. Uninstalling GHL disables GHL features for the agency; best-effort uninstall on the GHL side, local state cleared regardless.

Plan & limits

  • GHL connection is an agency-tier capability; client creation is bounded by sub-account capacity (see Subscriptions). Verify any GHL-specific plan gating.

Technical implementation

  • Views / components: frontend/src/views/agency/GhlInstructions.vue, frontend/src/components/agency/GhlInstallModal.vue, frontend/src/views/agency/companies/CreateGHL.vue, dashboard card in Dashboard.vue
  • Repository: frontend/src/repositories/agency/index.js (installGhl, uninstallGhl, installGhlForCompany, uninstallGhlFromCompany)
  • Service: backend/app/services/GoHighLevelService.js (getInstallUrl, installAppVersionForAgency, _appCreds, getAccessToken)
  • Controller: backend/app/controllers/AgencyController.js (createCompanyForGHL, unInstallGhl, loadAgencyAdminDataOnGHL)
  • Model: AgencyModel (go_high_level, go_high_level_install_version, go_high_level_name)
  • See also: CRM integration subsystem, GoHighLevel integration module, backend service