The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Voice Agent \u2014 Complete GHL Integration",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "ghl-voice-agent",
"responseMode": "responseNode",
"options": {}
},
"id": "0eb95e2e-5f0f-4ef7-8886-fe08bc0c05d4",
"name": "VAPI Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
0
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst toolCall = body.message.toolCalls[0];\nconst args = toolCall.function[\"arguments\"];\nconst parsed = typeof args === \"string\" ? JSON.parse(args) : args;\nconst nameParts = (parsed.name || '').trim().split(' ');\n\nfunction normalizeTime(t) {\n if (!t) return '';\n t = String(t).trim();\n const upper = t.toUpperCase();\n const isPM = /PM/i.test(upper);\n const isAM = /AM/i.test(upper);\n const cleaned = t.replace(/\\s*(AM|PM)\\s*/gi, '').trim();\n const parts = cleaned.split(':');\n let h = parseInt(parts[0], 10);\n let m = parts[1] ? parseInt(parts[1], 10) : 0;\n if (isNaN(h)) return '';\n if (isNaN(m)) m = 0;\n const hasLeadingZero = parts[0].length === 2 && parts[0][0] === '0';\n if (isAM) {\n if (h === 12) h = 0;\n } else if (isPM) {\n if (h !== 12) h += 12;\n } else if (!hasLeadingZero && h >= 1 && h <= 9) {\n h += 12;\n }\n return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0');\n}\n\nfunction cleanPhone(p) {\n if (!p) return '';\n p = String(p).trim();\n const hasPlus = p.startsWith('+');\n const digits = p.replace(/\\D/g, '');\n return hasPlus ? '+' + digits : digits;\n}\n\nreturn [{ json: { callId: toolCall.id, name: parsed.name || '', firstName: nameParts[0] || '', lastName: nameParts.slice(1).join(' ') || '', phone: cleanPhone(parsed.phone), service: parsed.service || 'Consultation', date: parsed.date || '', time: normalizeTime(parsed.time) } }];"
},
"id": "b4acd97c-5554-4c60-8fe6-e69a9bd18ac8",
"name": "Extract Book Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/contacts/upsert",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ \"firstName\": $('Extract Book Params').item.json.firstName, \"lastName\": $('Extract Book Params').item.json.lastName, \"phone\": $('Extract Book Params').item.json.phone, \"locationId\": \"YOUR_LOCATION_ID\", \"tags\": [\"voice-booking-ghl\"] }) }}",
"options": {}
},
"id": "0fa146a8-3d61-4a96-aaa4-48e94528316f",
"name": "Create GHL Contact1",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
448,
0
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/calendars/events/appointments",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ \"calendarId\": \"YOUR_CALENDAR_ID\", \"locationId\": \"YOUR_LOCATION_ID\", \"contactId\": $json.contact.id, \"title\": $('Extract Book Params').item.json.service + ' - ' + $('Extract Book Params').item.json.name, \"appointmentStatus\": \"confirmed\", \"startTime\": $('Extract Book Params').item.json.date + 'T' + $('Extract Book Params').item.json.time + ':00+05:00', \"endTime\": DateTime.fromISO($('Extract Book Params').item.json.date + 'T' + $('Extract Book Params').item.json.time + ':00', { zone: 'Asia/Karachi' }).plus({ minutes: 30 }).toISO() }) }}",
"options": {}
},
"id": "072574dd-14d4-4bd9-9586-91d55b8f705b",
"name": "Book GHL Calendar",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
672,
0
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/opportunities/",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ \"pipelineId\": \"YOUR_PIPELINE_ID\", \"locationId\": \"YOUR_LOCATION_ID\", \"contactId\": $('Create GHL Contact1').item.json.contact.id, \"name\": $('Extract Book Params').item.json.service + ' - ' + $('Extract Book Params').item.json.name, \"status\": \"open\", \"pipelineStageId\": \"YOUR_BOOKED_STAGE_ID\" }) }}",
"options": {}
},
"id": "f2ec8f94-fa72-455b-a90c-6e7effc24ecc",
"name": "Move Pipeline Booked",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
912,
-64
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const callId = $('Extract Book Params').first().json.callId;\nconst date = $('Extract Book Params').first().json.date;\nconst time = $('Extract Book Params').first().json.time;\nconst name = $('Extract Book Params').first().json.firstName;\nreturn [{ json: { results: [{ toolCallId: callId, result: 'Done! ' + name + ', your appointment is confirmed for ' + date + ' at ' + time + '. See you then!' }] } }];"
},
"id": "ffd1e6f7-e0a0-4627-a30b-8cc8b5b10d44",
"name": "Respond Book",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1440,
-64
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "737556e9-8363-4f19-b291-b112beaa5480",
"name": "Respond Book Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1680,
-64
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "ghl-complete-voice-agent",
"responseMode": "responseNode",
"options": {}
},
"id": "06ae45de-0ba1-430a-9ec8-fffe00633959",
"name": "GHL Voice Agent Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
224
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst toolCall = body.message.toolCalls[0];\nconst args = toolCall.function[\"arguments\"];\nconst parsed = typeof args === \"string\" ? JSON.parse(args) : args;\nreturn [{ json: { callId: toolCall.id, date: parsed.date || '' } }];"
},
"id": "18248119-904d-4998-9350-90a974e70dc7",
"name": "Extract Availability Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
224
]
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/calendars/YOUR_CALENDAR_ID/free-slots?startDate={{ new Date($('Extract Availability Params').item.json.date + 'T00:00:00').getTime() }}&endDate={{ new Date($('Extract Availability Params').item.json.date + 'T23:59:59').getTime() }}&timezone=Asia%2FKarachi",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "3c41ac23-eade-4e5e-9e4d-8aa694b048ed",
"name": "Get GHL Available Slots",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
448,
224
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const items = $input.all().map(i => i.json);\nconst callId = $('Extract Availability Params').first().json.callId;\n\nlet allSlots = [];\nfor (const data of items) {\n const keys = Object.keys(data).filter(k => k.match(/^\\d{4}-\\d{2}-\\d{2}$/));\n for (const key of keys) {\n const dayData = data[key];\n if (dayData && dayData.slots) {\n allSlots = allSlots.concat(dayData.slots);\n }\n }\n}\n\n// Convert to PKT (UTC+5) and filter business hours 9AM-5PM\nfunction toPKT(dt) {\n const d = new Date(dt);\n return new Date(d.getTime() + 5 * 60 * 60 * 1000);\n}\n\nfunction fmt(dt) {\n const pkt = toPKT(dt);\n let h = pkt.getUTCHours();\n const m = pkt.getUTCMinutes();\n const ap = h < 12 ? 'AM' : 'PM';\n if (h === 0) h = 12;\n else if (h > 12) h -= 12;\n return h + (m > 0 ? ':' + String(m).padStart(2,'0') : '') + ' ' + ap;\n}\n\nconst filtered = allSlots.filter(s => {\n const h = toPKT(s).getUTCHours();\n return h >= 9 && h < 17;\n});\n\nconst times = filtered.slice(0, 3).map(s => fmt(s));\nlet message;\nif (times.length === 0) {\n message = 'That day looks fully booked. Would another day work for you?';\n} else if (times.length === 1) {\n message = 'The only opening that day is ' + times[0] + '. Would that work?';\n} else {\n message = 'I have a few openings that day \u2014 ' + times.slice(0,-1).join(', ') + ' or ' + times[times.length-1] + '. Which suits you best?';\n}\n\nreturn [{ json: { results: [{ toolCallId: callId, result: message }] } }];"
},
"id": "9063036a-ee5f-4665-b9ec-4e10c7214ef7",
"name": "Format Availability Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
224
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "922ce56a-c04e-46fc-8df0-9227638e56b5",
"name": "Respond Availability",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
896,
224
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "ghl-get-booking",
"responseMode": "responseNode",
"options": {}
},
"id": "57cd5e05-2d6a-4897-9f16-f052e39c6809",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
448
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst toolCall = body.message.toolCalls[0];\nconst args = toolCall.function[\"arguments\"];\nconst parsed = typeof args === \"string\" ? JSON.parse(args) : args;\nreturn [{ json: { callId: toolCall.id, name: parsed.name || '', phone: parsed.phone || '' } }];"
},
"id": "1ae9ef7a-5a62-4020-ba72-71d6efdf0758",
"name": "Extract Get Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
448
]
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/contacts/?locationId=YOUR_LOCATION_ID&query={{ $json.name || $json.phone }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "a74dc3c9-ea75-4c18-a9d6-ea0c36efb1ba",
"name": "Search GHL Contact",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
448,
448
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconst callId = $('Extract Get Params').first().json.callId;\nconst contacts = data.contacts || [];\nif (contacts.length === 0) { return [{ json: { results: [{ toolCallId: callId, result: \"I couldn't find a booking under that name or number. Could you please provide both your full name and phone number?\" }] } }]; }\nconst contact = contacts[0];\nreturn [{ json: { results: [{ toolCallId: callId, result: 'I found your record \u2014 ' + (contact.firstName || '') + ' ' + (contact.lastName || '') + '. Would you like to reschedule or cancel your appointment?' }], contactId: contact.id } }];"
},
"id": "4118036c-02e5-4ed6-9645-31c2db4527b5",
"name": "Format Get Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
448
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "0fb31032-30a9-4521-89cc-e8a9b6ffa916",
"name": "Respond Get",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
896,
448
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "ghl-reschedule",
"responseMode": "responseNode",
"options": {}
},
"id": "bc969ab6-c6a3-44bb-aa14-e22f4ffc7bd4",
"name": "Webhook1",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
672
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst toolCall = body.message.toolCalls[0];\nconst args = toolCall.function[\"arguments\"];\nconst parsed = typeof args === \"string\" ? JSON.parse(args) : args;\n\nfunction normalizeTime(t) {\n if (!t) return '';\n t = String(t).trim();\n const upper = t.toUpperCase();\n const isPM = /PM/i.test(upper);\n const isAM = /AM/i.test(upper);\n const cleaned = t.replace(/\\s*(AM|PM)\\s*/gi, '').trim();\n const parts = cleaned.split(':');\n let h = parseInt(parts[0], 10);\n let m = parts[1] ? parseInt(parts[1], 10) : 0;\n if (isNaN(h)) return '';\n if (isNaN(m)) m = 0;\n // If already in 24hr format (has leading zero like 09 or h>=12) leave as-is\n const hasLeadingZero = parts[0].length === 2 && parts[0][0] === '0';\n if (isAM) {\n if (h === 12) h = 0;\n } else if (isPM) {\n if (h !== 12) h += 12;\n } else if (!hasLeadingZero && h >= 1 && h <= 9) {\n // Only ambiguous single-digit hours get PM treatment\n h += 12;\n }\n // If h >= 17 and no explicit PM was given, likely wrong - cap at business hours\n return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0');\n}\n\nreturn [{ json: { callId: toolCall.id, name: parsed.name || '', phone: parsed.phone || '', newDate: parsed.newDate || parsed.date || '', newTime: normalizeTime(parsed.newTime || parsed.time || '') } }];"
},
"id": "cf239a85-0776-427b-8d6b-ab8a7f240f4c",
"name": "Extract Reschedule Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
672
]
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/contacts/?locationId=YOUR_LOCATION_ID&query={{ $json.name || $json.phone }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "f1dadd8d-39cd-4752-8368-0af3468e0fa5",
"name": "Find Contact Reschedule",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
448,
672
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/contacts/{{ $json.contacts[0].id }}/appointments",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "888ffb2b-ef09-48a3-923f-0d019621314c",
"name": "Get Contact Appointments",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
672,
672
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "DELETE",
"url": "=https://services.leadconnectorhq.com/calendars/events/appointments/{{ $json.events && $json.events[0] ? $json.events[0].id : '' }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "ccb16be0-0b3d-4cce-a365-4ba9e9883227",
"name": "Delete Old Appointment",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
896,
672
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/calendars/events/appointments",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ \"calendarId\": \"YOUR_CALENDAR_ID\", \"locationId\": \"YOUR_LOCATION_ID\", \"contactId\": $('Find Contact Reschedule').item.json.contacts[0].id, \"title\": \"Rescheduled - \" + $('Extract Reschedule Params').item.json.name, \"appointmentStatus\": \"confirmed\", \"startTime\": $('Extract Reschedule Params').item.json.newDate + 'T' + $('Extract Reschedule Params').item.json.newTime + ':00+05:00', \"endTime\": DateTime.fromISO($('Extract Reschedule Params').item.json.newDate + 'T' + $('Extract Reschedule Params').item.json.newTime + ':00', { zone: 'Asia/Karachi' }).plus({ minutes: 30 }).toISO() }) }}",
"options": {}
},
"id": "7899677f-b788-490f-97fa-cebfc781ec80",
"name": "Create New Appointment",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1120,
672
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const callId = $('Extract Reschedule Params').first().json.callId;\nconst newDate = $('Extract Reschedule Params').first().json.newDate;\nconst newTime = $('Extract Reschedule Params').first().json.newTime;\nreturn [{ json: { results: [{ toolCallId: callId, result: 'Done! Your appointment has been rescheduled to ' + newDate + ' at ' + newTime + '. You will receive an SMS confirmation shortly.' }] } }];"
},
"id": "d605bc3a-9c2c-445a-b5b3-53d4d38118f6",
"name": "Respond Reschedule",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1344,
672
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "03a77a64-7838-4d26-9ccc-c717de4f30bb",
"name": "Respond Reschedule Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1568,
672
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "ghl-cancel",
"responseMode": "responseNode",
"options": {}
},
"id": "3fcc2de9-d803-42e6-a027-5e2202458fc4",
"name": "Webhook2",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
896
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst toolCall = body.message.toolCalls[0];\nconst args = toolCall.function[\"arguments\"];\nconst parsed = typeof args === \"string\" ? JSON.parse(args) : args;\nreturn [{ json: { callId: toolCall.id, name: parsed.name || '', phone: parsed.phone || '' } }];"
},
"id": "675d4b0b-8ed5-4a23-aa69-a6815059aa66",
"name": "Extract Cancel Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
896
]
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/contacts/?locationId=YOUR_LOCATION_ID&query={{ $json.name || $json.phone }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "02ace81a-d8a6-4dd4-a26d-b7dd6e8d6f36",
"name": "Find Contact Cancel",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
448,
896
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/contacts/{{ $json.contacts[0].id }}/appointments",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "08ed8040-c36a-49bc-bba8-52c21b96937c",
"name": "Get Appointment To Cancel",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
672,
896
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "DELETE",
"url": "=https://services.leadconnectorhq.com/calendars/events/appointments/{{ $json.events && $json.events[0] ? $json.events[0].id : '' }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
}
]
},
"options": {}
},
"id": "6278c4b9-83c8-4da3-9c39-ebebefde8363",
"name": "Delete Appointment",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
896,
896
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "=https://services.leadconnectorhq.com/opportunities/search?location_id=YOUR_LOCATION_ID&contact_id={{ $('Find Contact Cancel').item.json.contacts[0].id }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"options": {}
},
"id": "8a32bfb0-b334-4506-a232-9cd1c3d19ab0",
"name": "Move Pipeline Cancelled",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1120,
896
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const callId = $('Extract Cancel Params').first().json.callId;\nreturn [{ json: { results: [{ toolCallId: callId, result: 'Done. Your appointment has been cancelled and updated in our system. We hope to see you again soon. Take care!' }] } }];"
},
"id": "05650e63-b8c0-4814-b636-c0a93c99e9d1",
"name": "Respond Cancel",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1616,
896
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "8b32e3f0-8f7e-4ded-b98b-f8e891641ab6",
"name": "Respond Cancel Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1888,
896
]
},
{
"parameters": {
"method": "PUT",
"url": "=https://services.leadconnectorhq.com/opportunities/{{ $json.opportunities[0].id }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"status\": \"lost\", \"pipelineStageId\": \"YOUR_CANCELLED_STAGE_ID\"}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1312,
896
],
"id": "4092bcf4-f3a7-463c-9791-143557d69107",
"name": "Update Opportunity Cancelled",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const callId = $('Extract Book Params').first().json.callId;\nconst date = $('Extract Book Params').first().json.date;\nreturn [{ json: { results: [{ toolCallId: callId, result: \"I'm sorry, that time slot is no longer available. Let me offer you different options \u2014 would another time on \" + date + \" work, or would you prefer a different day altogether?\" }] } }];"
},
"id": "29517e9a-5c82-4705-a9ee-8ff6f37ec410",
"name": "Handle Slot Unavailable",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1280,
160
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"results\": $json.results } }}",
"options": {}
},
"id": "cd4f9be6-2111-4f59-8689-e13b6412b01e",
"name": "Respond Slot Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1712,
160
]
},
{
"parameters": {
"method": "POST",
"url": "https://services.leadconnectorhq.com/conversations/messages",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Version",
"value": "2021-07-28"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ \"type\": \"SMS\", \"contactId\": $('Create GHL Contact1').item.json.contact.id, \"locationId\": \"YOUR_LOCATION_ID\", \"message\": \"Hi \" + $('Extract Book Params').item.json.firstName + \", your \" + $('Extract Book Params').item.json.service + \" appointment is confirmed for \" + $('Extract Book Params').item.json.date + \" at \" + $('Extract Book Params').item.json.time + \". See you soon! - BrightSmile Dental\" }) }}",
"options": {}
},
"id": "6c9e5417-01b2-441e-93ad-148aac2bb9e2",
"name": "Send Booking SMS",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1136,
-64
],
"alwaysOutputData": true,
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
}
],
"connections": {
"VAPI Webhook": {
"main": [
[
{
"node": "Extract Book Params",
"type": "main",
"index": 0
}
]
]
},
"Extract Book Params": {
"main": [
[
{
"node": "Create GHL Contact1",
"type": "main",
"index": 0
}
]
]
},
"Create GHL Contact1": {
"main": [
[
{
"node": "Book GHL Calendar",
"type": "main",
"index": 0
}
]
]
},
"Book GHL Calendar": {
"main": [
[
{
"node": "Move Pipeline Booked",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Slot Unavailable",
"type": "main",
"index": 0
}
]
]
},
"Move Pipeline Booked": {
"main": [
[
{
"node": "Send Booking SMS",
"type": "main",
"index": 0
}
]
]
},
"Respond Book": {
"main": [
[
{
"node": "Respond Book Webhook",
"type": "main",
"index": 0
}
]
]
},
"GHL Voice Agent Webhook": {
"main": [
[
{
"node": "Extract Availability Params",
"type": "main",
"index": 0
}
]
]
},
"Extract Availability Params": {
"main": [
[
{
"node": "Get GHL Available Slots",
"type": "main",
"index": 0
}
]
]
},
"Get GHL Available Slots": {
"main": [
[
{
"node": "Format Availability Response",
"type": "main",
"index": 0
}
]
]
},
"Format Availability Response": {
"main": [
[
{
"node": "Respond Availability",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Extract Get Params",
"type": "main",
"index": 0
}
]
]
},
"Extract Get Params": {
"main": [
[
{
"node": "Search GHL Contact",
"type": "main",
"index": 0
}
]
]
},
"Search GHL Contact": {
"main": [
[
{
"node": "Format Get Response",
"type": "main",
"index": 0
}
]
]
},
"Format Get Response": {
"main": [
[
{
"node": "Respond Get",
"type": "main",
"index": 0
}
]
]
},
"Webhook1": {
"main": [
[
{
"node": "Extract Reschedule Params",
"type": "main",
"index": 0
}
]
]
},
"Extract Reschedule Params": {
"main": [
[
{
"node": "Find Contact Reschedule",
"type": "main",
"index": 0
}
]
]
},
"Find Contact Reschedule": {
"main": [
[
{
"node": "Get Contact Appointments",
"type": "main",
"index": 0
}
]
]
},
"Get Contact Appointments": {
"main": [
[
{
"node": "Delete Old Appointment",
"type": "main",
"index": 0
}
]
]
},
"Delete Old Appointment": {
"main": [
[
{
"node": "Create New Appointment",
"type": "main",
"index": 0
}
]
]
},
"Create New Appointment": {
"main": [
[
{
"node": "Respond Reschedule",
"type": "main",
"index": 0
}
]
]
},
"Respond Reschedule": {
"main": [
[
{
"node": "Respond Reschedule Webhook",
"type": "main",
"index": 0
}
]
]
},
"Webhook2": {
"main": [
[
{
"node": "Extract Cancel Params",
"type": "main",
"index": 0
}
]
]
},
"Extract Cancel Params": {
"main": [
[
{
"node": "Find Contact Cancel",
"type": "main",
"index": 0
}
]
]
},
"Find Contact Cancel": {
"main": [
[
{
"node": "Get Appointment To Cancel",
"type": "main",
"index": 0
}
]
]
},
"Get Appointment To Cancel": {
"main": [
[
{
"node": "Delete Appointment",
"type": "main",
"index": 0
}
]
]
},
"Delete Appointment": {
"main": [
[
{
"node": "Move Pipeline Cancelled",
"type": "main",
"index": 0
}
]
]
},
"Move Pipeline Cancelled": {
"main": [
[
{
"node": "Update Opportunity Cancelled",
"type": "main",
"index": 0
}
]
]
},
"Respond Cancel": {
"main": [
[
{
"node": "Respond Cancel Webhook",
"type": "main",
"index": 0
}
]
]
},
"Update Opportunity Cancelled": {
"main": [
[
{
"node": "Respond Cancel",
"type": "main",
"index": 0
}
]
]
},
"Handle Slot Unavailable": {
"main": [
[
{
"node": "Respond Slot Error",
"type": "main",
"index": 0
}
]
]
},
"Send Booking SMS": {
"main": [
[
{
"node": "Respond Book",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": true
},
"versionId": "a9c2835a-51c5-4744-a277-82947591586a",
"meta": {
"aiBuilderAssisted": true,
"builderVariant": "mcp",
"templateCredsSetupCompleted": true
},
"nodeGroups": [],
"id": "HwIggz54RsnZA03G",
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Voice Agent — Complete GHL Integration. Uses httpRequest. Webhook trigger; 37 nodes.
Source: https://github.com/Usman-rai/ghl-ai-voice-receptionist/blob/main/workflow/ghl-voice-receptionist.n8n.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Jigsaw API key for image processing, I use this as a gatekeeper/second pair of eyes. LINK to their website https://jigsawstack.com/ SECOND A postgress DATABASE (I use Supabase) LlamaCloud for the pars
Onsite Photos to Jobs (SMS Agent). Uses dataTable, twilio, httpRequest, airtable. Webhook trigger; 62 nodes.
W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 50 nodes.
W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 48 nodes.
WF-OB-2: Post-Call Handler. Uses httpRequest, slack. Webhook trigger; 48 nodes.