Go High Level (GHL) Integration
Trigger a Volume Reach qualification call from any GHL workflow when a new lead arrives, and pipe the outcome (interested? appointment time? transcript?) back into the GHL contact record — moving deals forward automatically.
Time to build: ~20 minutes. GHL plan requirement: any plan with Workflows + Inbound Webhooks (Standard, Unlimited, Agency Pro).
The flow
GHL: New Form Submission → Workflow: Webhook action → POST /api/v1/public/calls
│
▼
Volume Reach dials + qualifies
│
▼
call.completed webhook → GHL Inbound Webhook
│
▼
Update contact, move pipeline stage,
create appointment
Two GHL workflows total: one outbound (fires the call) and one inbound (receives the result and updates the contact). They're decoupled because Volume Reach runs asynchronously — the dial, conversation, transcription, and extraction together take 1–3 minutes.
Part 1: One-time setup in Volume Reach (5 min)
Step 1 — Create an API key
- Settings → API Keys → Create.
- Name it something descriptive:
GHL Production. - Check these scopes:
calls:place,calls:read,webhooks. - Click Create. Copy the
vr_live_...key immediately — it's shown only once. Paste it into your password manager or GHL's secret store.
Step 2 — Configure your Voice Agent
Make sure the agent you'll use is set up with a system prompt that asks the right qualifying questions (budget, timeline, interest level, appointment availability).
- Settings → Voice Agents → Edit.
- In the Prompt tab, confirm your
systemPromptreferences any{{variableName}}you plan to pass in from GHL. For example:Hi {{leadName}}, I'm calling from Acme about the quote request for {{propertyAddress}}. - In the Extraction tab (if available), pick a template (e.g.
Appointment Booking) or define custom fields. The schema determines what GHL receives back. See Structured Extraction. - Copy the agent's
id(e.g.va_abc123...) from the URL bar.
Step 3 — Create the webhook endpoint
- Automation → Webhook Endpoints → Create.
- Name:
GHL Inbound. - URL: the Inbound Webhook URL from GHL (see Part 3 below — if you haven't set up GHL yet, just use
https://webhook.site/<your-test-id>temporarily and come back to update). - Method: POST.
- Click Create. Save the returned
signingSecret— you won't see it again.
Step 4 — Subscribe to call.completed
- Automation → Triggers → Create.
- Name:
GHL — All Qualified Calls. - Event type:
call.completed. - Webhook endpoint: the one you created in step 3.
- Conditions (optional): restrict to
outcome: ["INTERESTED", "APPOINTMENT"]if you only want to fire GHL updates for hot leads. - Save.
Part 2: GHL outbound workflow (fires the call)
Sub-account → Automation → Workflows → New Workflow → start from scratch.
Trigger
Pick whichever GHL event should trigger a Volume Reach qualification call:
- Form Submitted (most common — a lead fills out a landing page form).
- Contact Created (any new contact in your sub-account).
- Opportunity Stage Changed (e.g. moving to "Needs Qualification").
- Tag Added (e.g. when you tag a contact
needs-qualification).
Filters (recommended)
Add filters so the workflow only fires when you actually want a call:
Contact > Phoneis not empty.Contact > Phone Type= "mobile" (optional — avoid calling landlines that can't receive SMS follow-ups).- Wait step: set a short delay (e.g. 30 seconds) to give GHL time to finish contact creation before you trigger the call. This avoids race conditions.
Action: Webhook
Add a Webhook action. Configure as follows:
URL:
https://api.volumereach.com/api/v1/public/calls
Method: POST
Headers (use GHL's header editor):
| Key | Value |
|---|---|
Authorization | Bearer vr_live_... (paste your key) |
Content-Type | application/json |
Body (JSON — use GHL's custom-value dropdowns for each {{...}}):
{
"agentId": "va_abc123",
"phoneNumber": "{{contact.phone}}",
"variables": {
"leadName": "{{contact.full_name}}",
"leadEmail": "{{contact.email}}",
"leadSource": "ghl-{{workflow.name}}",
"propertyAddress": "{{contact.address1}}, {{contact.city}} {{contact.state}}"
},
"metadata": {
"ghlContactId": "{{contact.id}}",
"ghlLocationId": "{{location.id}}",
"ghlWorkflowId": "{{workflow.id}}"
},
"idempotencyKey": "ghl-{{contact.id}}-{{workflow.id}}",
"knownConsent": true,
"consentRecordId": "ghl-form-{{contact.id}}"
}
Idempotency is not optional for GHL. GHL retries webhooks on transient errors. Without
idempotencyKey, a network blip = 2 calls to the same lead. The key pattern above (ghl-<contactId>-<workflowId>) makes the request idempotent across retries.
Save + publish
- Name the workflow clearly:
Volume Reach — Qualify New Leads. - Toggle it Active.
Part 3: GHL inbound workflow (receives call.completed)
GHL's Inbound Webhook trigger gives you a URL that fires a workflow whenever anything POSTs to it. Volume Reach will POST here when the call finishes.
Step 1 — Create the custom fields
Before the workflow can write results back to the contact, create the custom fields Contacts → Custom Fields:
| Field name | Data type | Purpose |
|---|---|---|
VR Last Call ID | Single line | Link from contact to Volume Reach call record |
VR Last Outcome | Dropdown | INTERESTED, NOT_INTERESTED, CALLBACK, NO_ANSWER, BUSY, VOICEMAIL, WRONG_NUMBER, DNC |
VR Last Summary | Multi-line text | One-sentence summary from the AI agent |
VR Last Transcript | Multi-line text | Full call transcript (capped at 10 000 chars) |
VR Appointment (ISO) | Single line or Date | Confirmed appointment time |
VR Budget Range | Dropdown | under_5k, 5k_to_15k, 15k_to_50k, 50k_plus, not_mentioned |
VR Interested | Boolean | From extraction schema |
Your extraction schema should produce fields that match. See Structured Extraction for template schemas.
Step 2 — Create the inbound workflow
Automation → Workflows → New Workflow.
Trigger: Inbound Webhook.
GHL generates a URL that looks like:
https://services.leadconnectorhq.com/hooks/<location_id>/webhook-trigger/<unique>
Copy that URL and paste it into Part 1 → Step 3 (Volume Reach webhook endpoint) if you haven't already.
Step 3 — Test the trigger
In Volume Reach, place a test call via:
- Automation → Public API Dashboard → Test Call (once that page ships), or
- A curl invocation of
POST /api/v1/public/callsdialing your own phone.
Complete the test call. GHL will receive the call.completed webhook and display a sample payload. Click "Use this sample" so GHL knows the field shape.
Step 4 — Build the workflow actions
Add these actions in order:
A. If / Else — "Is this call from the Public API?"
- Condition:
{{inboundWebhookRequest.event}}equalscall.completed. - If false, end the workflow.
B. Find Contact by ID
- Search field: Contact ID.
- Value:
{{inboundWebhookRequest.data.metadata.ghlContactId}}. - This locates the contact the outbound workflow dialed.
C. Update Contact Custom Fields
| Field | Value |
|---|---|
VR Last Call ID | {{inboundWebhookRequest.data.call_id}} |
VR Last Outcome | {{inboundWebhookRequest.data.outcome}} |
VR Last Summary | {{inboundWebhookRequest.data.extraction.summary}} |
VR Last Transcript | {{inboundWebhookRequest.data.transcript}} |
VR Appointment (ISO) | {{inboundWebhookRequest.data.extraction.appointmentDateTimeISO}} |
VR Budget Range | {{inboundWebhookRequest.data.extraction.budgetRange}} |
VR Interested | {{inboundWebhookRequest.data.extraction.interested}} |
D. If / Else — "Was the lead interested?"
- Condition:
{{inboundWebhookRequest.data.extraction.interested}}=true. - True branch: continue to actions E, F, G.
- False branch: add tag
needs-nurture, end workflow.
E. Add Tag: qualified-lead.
F. Move Contact in Pipeline: to your "Sales Qualified" stage.
G. Create Appointment (if appointmentDateTimeISO is set):
- Condition first:
{{inboundWebhookRequest.data.extraction.appointmentDateTimeISO}}is not empty. - Calendar: your sales calendar.
- Start time:
{{inboundWebhookRequest.data.extraction.appointmentDateTimeISO}}. - Duration: 60 min (or
{{inboundWebhookRequest.data.extraction.appointmentDurationMinutes}}if your schema exposes it). - Description: paste the summary + a link back to the call ID for transcript lookup.
H. Send Internal Notification (Slack / email to sales team):
🔥 New qualified lead from Volume Reach
Name: {{contact.full_name}}
Phone: {{contact.phone}}
Outcome: {{inboundWebhookRequest.data.outcome}}
Summary: {{inboundWebhookRequest.data.extraction.summary}}
Appointment: {{inboundWebhookRequest.data.extraction.appointmentDateTimeISO}}
Step 5 — Publish + activate
Name the workflow: Volume Reach — Process Call Result. Toggle Active.
End-to-end test
- Create a test form submission in GHL with your own cell phone number and a test email.
- Within ~30 seconds (the delay step), your phone should ring.
- Have a short conversation with the AI agent — something like "Yes I'm interested, budget around $10k, Tuesday 2pm works for an appointment."
- Hang up.
- Within 60 seconds, check the GHL contact record. The custom fields should be populated. The pipeline stage should have moved. A calendar appointment should exist for Tuesday 2pm.
If any step didn't fire:
- Volume Reach → Automation → Deliveries (once dashboard ships) shows the delivery attempts with status codes.
- GHL → Workflow → Execution Logs shows whether the inbound webhook triggered the workflow and which actions ran.
Payload reference (what GHL receives)
Complete call.completed payload so you can reference field paths while wiring actions:
{
"event": "call.completed",
"timestamp": "2026-04-23T14:05:47.123Z",
"data": {
"call_id": "call_xyz",
"outcome": "INTERESTED",
"duration_seconds": 127,
"started_at": "2026-04-23T14:03:41.000Z",
"recording_url": null,
"transcript": "Agent: Hi Jane...\nJane: Yes, I'm interested...\n(full transcript, capped at 10000 chars)",
"contact": {
"name": "Jane Smith",
"phone": "+15551234567",
"address": "123 Oak St",
"city": "Springfield",
"state": "IL",
"zip": "62704"
},
"variables": {
"leadName": "Jane Smith",
"leadEmail": "jane@example.com",
"leadSource": "ghl-Volume Reach — Qualify New Leads",
"propertyAddress": "123 Oak St, Springfield IL"
},
"metadata": {
"ghlContactId": "<contact-id>",
"ghlLocationId": "<location-id>",
"ghlWorkflowId": "<workflow-id>"
},
"known_consent": true,
"consent_record_id": "ghl-form-<contact-id>",
"extraction": {
"interested": true,
"wantsAppointment": true,
"appointmentDateTimeISO": "2026-04-28T18:00:00Z",
"appointmentDurationMinutes": 60,
"budgetRange": "5k_to_15k",
"summary": "Homeowner interested in a roof estimate, $5-15k budget, wants Tuesday 2pm site visit."
},
"extraction_error": null
}
}
Field-by-field docs: Webhooks. Extraction authoring: Structured Extraction.
Common pitfalls
Phone numbers in non-E.164 format. GHL sometimes stores phones as (555) 123-4567. Volume Reach normalizes automatically via normalizeE164, but if your tenant has international numbers, pre-format with GHL's phone-parser custom field or Volume Reach will reject the request with invalid_phone_number.
Workflow infinite loops. If your inbound workflow adds a tag that matches the outbound workflow's trigger filter, you'll re-trigger the call forever. Guard with a contact-level check: "Only fire if VR Last Call ID is empty" on the outbound workflow, or use a one-time trigger condition.
Missing consent. GHL lead forms must include a TCPA-compliant opt-in checkbox ("I agree to be contacted by phone about my quote request"). Without a documented opt-in, do not set knownConsent: true. Volume Reach logs your attestation but you remain legally responsible.
Over-dialing a bad lead. If someone opts out mid-call (outcome DNC), Volume Reach marks the call but doesn't update GHL automatically. Your inbound workflow should explicitly check {{inboundWebhookRequest.data.outcome}} for DNC, and if present, add the contact to a suppression list, remove them from active pipelines, and do NOT trigger any follow-up workflows.
Rate limits. Volume Reach throttles at 60 POSTs/min per API key for write routes. If you're bulk-importing 500 leads and all trigger the workflow at once, you'll see 429 rate_limited. Add a delay step to spread out the triggers, or split the import.
Webhook delivery retries. If your GHL inbound workflow is temporarily broken, Volume Reach retries with exponential backoff (1m / 5m / 30m / 2h / 6h / 24h — 6 attempts). Failed deliveries show up in Automation → Deliveries where you can replay them after fixing the GHL workflow.
What's next
- Extraction schema authoring — tune the exact fields your GHL workflow receives.
- Webhooks — signature verification, retry behavior, DLQ replay.
- FAQ — troubleshooting specific errors and support process.
- API Reference — full endpoint list + error codes.