GoHighLevel (GHL) Overview
GoHighLevel is the execution layer for all outbound calling in Client Portal. It manages phone dialing, call recording, transcript generation, and BDR workflow automation. Client Portal integrates bidirectionally: pushing prospects into GHL for calling, and pulling call data back via webhooks and API.
API Configuration
| Setting | Value |
|---|---|
| API Base URL | https://services.leadconnectorhq.com |
| API Version | 2021-07-28 |
| Auth | Bearer token (per-location) |
| Version Header | Version: 2021-07-28 |
const headers = {
Authorization: `Bearer ${apiToken}`,
Version: "2021-07-28",
Accept: "application/json",
};
Architecture
GHL operates on a location-based model. Each GHL "location" represents an isolated account with its own contacts, workflows, and API token. Client Portal maps these locations to either internal sales operations or client campaigns via the ghl_integrations table.
ghl_integrations Table
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
location_id | TEXT | GHL location ID (unique) |
api_token | TEXT | Bearer token for this location |
integration_type | TEXT | internal or client |
client_id | UUID | FK to clients (null for internal) |
custom_field_mapping | JSONB | Maps our fields to GHL custom field IDs |
is_active | BOOLEAN | Whether this integration is live |
last_synced_at | TIMESTAMPTZ | Last successful sync timestamp |
last_sync_status | TEXT | success, partial, or failed |
last_sync_error | TEXT | Error details from last sync |
Integration Types
| Type | Purpose | Destination Tables |
|---|---|---|
internal | 1HR's own sales prospecting | sales_prospects, sales_calls |
client | Client campaign calling | contacts, calls |
Integration Flow
Bidirectional Data Flow
Outbound: Client Portal to GHL
-
Prospect Sync (
sync-prospects-to-ghl): Pushes unsyncedsales_prospectsto GHL as contacts. Maps custom fields (LinkedIn, job title, market name, cadence tracking). Storesghl_contact_idback on the prospect. -
Custom Field Mapping: Each GHL location has its own custom field IDs. The
custom_field_mappingJSONB column onghl_integrationsmaps logical field names to GHL field IDs:
{
"linkedin_url": "GHL_FIELD_ID_1",
"job_title": "GHL_FIELD_ID_2",
"market_name": "GHL_FIELD_ID_3",
"cadence_stage": "GHL_FIELD_ID_4",
"dial_attempts": "GHL_FIELD_ID_5",
"no_answer_count": "GHL_FIELD_ID_6",
"voicemail_count": "GHL_FIELD_ID_7",
"callback_attempts": "GHL_FIELD_ID_8"
}
Inbound: GHL to Client Portal
-
Webhooks (
ghl-call-webhook): GHL workflows fire webhook payloads on call completion and BDR disposition selection. The webhook handler normalizes the payload, identifies the integration type, enriches via GHL API, and routes to the correct table. -
Batch Import (
pull-from-ghl): Paginates through all contacts in a GHL location, stages them inghl_import_staging, fetches call history via the Conversations API, runs duplicate detection, and presents for review.
GHL API Endpoints Used
| Endpoint | Method | Purpose |
|---|---|---|
/contacts/ | POST | Create new contact |
/contacts/?locationId=... | GET | List contacts (paginated) |
/conversations/search?contactId=... | GET | Find conversations for a contact |
/conversations/{id}/messages | GET | Get messages in a conversation |
/conversations/locations/{id}/messages/{id}/transcription/download | GET | Download call transcript |
Environment Secrets
| Secret | Description |
|---|---|
SUPABASE_URL | Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY | Service role key for DB access |
ANTHROPIC_API_KEY | For AI call summarization (auto-triggered) |
GHL API tokens are stored in ghl_integrations.api_token. These are sensitive credentials -- never expose them in client-side code. All GHL API calls happen exclusively in Supabase edge functions using the service role key.
Related Documentation
- GHL Webhooks -- Webhook payload formats and disposition inference
- GHL Prospect Sync -- Push/pull sync mechanics
- Anthropic AI -- AI call summarization triggered by GHL webhooks