GHL Integration Tables
These tables support the GoHighLevel (GHL) integration for outbound calling automation. GHL is used as the execution layer for calls -- contacts are synced to GHL, calls are made in GHL, and call data flows back via webhooks.
ghl_integrations
Stores GHL location credentials and custom field mappings. Each GHL "location" (sub-account) gets its own row.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | uuid | NO | gen_random_uuid() | Primary key |
location_id | text | NO | GHL location ID (unique) | |
api_token | text | NO | GHL API bearer token | |
name | text | YES | Human-readable location name | |
is_active | boolean | YES | true | Whether this integration is active |
custom_field_mapping | jsonb | YES | Maps Client Portal fields to GHL custom field IDs | |
created_at | timestamptz | YES | now() | |
updated_at | timestamptz | YES | now() |
Custom Field Mapping
The custom_field_mapping JSONB maps Client Portal field names to GHL custom field IDs. This allows the sync-prospects-to-ghl function to populate custom fields when creating contacts in GHL.
{
"company_name": "custom_field_abc123",
"contact_title": "custom_field_def456",
"source": "custom_field_ghi789",
"linkedin_url": "custom_field_jkl012"
}
GHL API Details
| Setting | Value |
|---|---|
| Base URL | https://services.leadconnectorhq.com |
| API Version | 2021-07-28 |
| Auth | Bearer token from ghl_integrations.api_token |
ghl_import_staging
Temporary staging table for batch imports from GHL. Records are staged here, matched to existing prospects/contacts, then processed.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | uuid | NO | gen_random_uuid() | Primary key |
batch_id | uuid | NO | FK to ghl_import_batches.id | |
raw_data | jsonb | NO | Raw GHL contact/call data | |
status | text | YES | 'pending' | Processing status: pending, matched, imported, error, skipped |
error_message | text | YES | Error details if processing failed | |
matched_prospect_id | uuid | YES | FK to sales_prospects.id (if matched) | |
matched_contact_id | uuid | YES | FK to contacts.id (if matched) | |
created_at | timestamptz | YES | now() |
ghl_import_batches
Metadata for import batches. Each batch represents a single pull from GHL.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | uuid | NO | gen_random_uuid() | Primary key |
location_id | text | NO | GHL location ID | |
status | text | YES | 'pending' | pending, processing, completed, failed |
total_records | integer | YES | Total records in batch | |
processed_records | integer | YES | 0 | Records processed so far |
error_count | integer | YES | 0 | Number of errors |
created_by | uuid | YES | FK to auth.users.id | |
completed_at | timestamptz | YES | When processing finished | |
created_at | timestamptz | YES | now() |
GHL Data Flow
GHL fires two webhook hits per call -- one from the "Call Completed" workflow and one from the "Disposition" workflow. Both carry the same callId. The edge function deduplicates using external_call_id:
- First hit: INSERT new
sales_callsrecord - Second hit: UPDATE existing record with any additional data (transcript, disposition)
GHL callStatus values are mixed case: Answered (715 occurrences), No answer (72), Busy (25), Failed (24), completed (23), Voicemail (4), Missed (1), Ringing (1). The webhook function normalizes all values to lowercase before comparing. Both "answered" and "completed" mean someone picked up.