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:
installGhl()→POST /go-high-level/install-url/agencyreturns a GHL OAuth URL.- 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). - GHL redirects back to
/ghlinstallation/install; the callback exchanges the code, then bounces to the agency dashboard withintegration=go-high-level&success=true. - 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 globalgoHighLevel.new_install_app_versionconfig (defaultsv1). GoHighLevelService.getInstallUrlresolves credentials via_appCreds(version)and carries the chosenapp_versionthrough OAuthstateso the callback exchanges + stamps with the same app.- Existing tokens always refresh against the
app_versionstored ongo_high_level(orghl_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
| Action | Repository fn | Endpoint | Gate |
|---|---|---|---|
| Get agency install URL | installGhl() | POST /go-high-level/install-url/agency | agency.manage_settings (UI card) |
| Uninstall agency | uninstallGhl(agencyId) | POST /go-high-level/uninstall/agency | confirm dialog |
| Install for a company | installGhlForCompany(companyId) | POST /go-high-level/install-url | — |
| Uninstall from a company | uninstallGhlFromCompany(companyId) | POST /go-high-level/uninstall | — |
agency.go_high_level fields (AgencyModel)
| Field | Notes |
|---|---|
access_token, refresh_token, access_token_expires_at | OAuth tokens |
user_type, user_id, company_id | GHL identifiers |
custom_menu_id | GHL custom menu link |
app_version | Which marketplace app minted the token (v1 default / v2) — refresh uses this |
Install-version override (AgencyModel)
| Field | Notes |
|---|---|
go_high_level_install_version | Super-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_versionoff the stored token specifically to avoidinvalid_grant. - Override ≠ migration. Setting
go_high_level_install_versiononly 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 inDashboard.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
Related
- Agency Portal — Overview
- Managing Companies — create-from-GHL flow
- Dashboard — the Connect-GHL card
- Subscriptions — sub-account capacity