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": "Phase 5 - Confirmation Call (Vapi)",
"nodes": [
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "b86d5c8d-ee9c-4063-bfb9-b68a7fb0cbaf",
"name": "supabaseUrl",
"value": "https://YOUR_PROJECT_REF.supabase.co",
"type": "string"
},
{
"id": "3f5081fc-0cd7-4ba6-82df-997266bfb744",
"name": "vapiAssistantId",
"value": "YOUR_VAPI_ASSISTANT_ID",
"type": "string"
},
{
"id": "c6a778a8-dc49-4d0b-871e-0e9c3d1fdafb",
"name": "vapiPhoneNumberId",
"value": "YOUR_VAPI_PHONE_NUMBER_ID",
"type": "string"
},
{
"id": "015a2564-053c-444f-a3b4-d8eceb86bfa9",
"name": "testNumber",
"value": "+10000000000",
"type": "string"
}
]
},
"options": {}
},
"id": "40494942-3cdf-485f-96a9-e03f61f6cf83",
"name": "Config",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
784,
2304
]
},
{
"parameters": {
"method": "PATCH",
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/meetings?id=eq.' + $json.meetingId }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"status\": \"confirmed\" } }}",
"options": {}
},
"id": "9fa20730-a08d-4339-b3d6-815ca49fd0d2",
"name": "Set Meeting Confirmed",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1952,
2400
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/calls' }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"contact_id\": $json.contactId, \"call_type\": \"confirmation\", \"outcome\": \"confirmed\", \"vapi_call_id\": $json.vapiCallId, \"transcript_summary\": \"Confirmation call placed to the test number via Vapi. (Outcome shown here is simulated; a live end-of-call webhook would write the real outcome + transcript.)\" } }}",
"options": {}
},
"id": "d086e281-0a9b-45a2-87bf-dfc2854a16e3",
"name": "Log Call",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1952,
2192
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const src = $('Build Call').all();\nfunction extractText(r) {\n if (r == null) return '';\n if (typeof r === 'string') return r;\n if (Array.isArray(r.output)) {\n let acc = '';\n for (const msg of r.output) {\n if (msg && Array.isArray(msg.content)) {\n for (const c of msg.content) { if (c && typeof c.text === 'string') acc += c.text; }\n } else if (typeof msg === 'string') { acc += msg; }\n else if (msg && typeof msg.text === 'string') { acc += msg.text; }\n }\n if (acc) return acc;\n }\n if (typeof r.output_text === 'string') return r.output_text;\n if (typeof r.output === 'string') return r.output;\n if (typeof r.content === 'string') return r.content;\n if (typeof r.text === 'string') return r.text;\n if (r.message && typeof r.message.content === 'string') return r.message.content;\n if (r.choices && r.choices[0] && r.choices[0].message && typeof r.choices[0].message.content === 'string') return r.choices[0].message.content;\n if (Array.isArray(r.content)) return r.content.map(b => (b && b.text) ? b.text : '').join('');\n return '';\n}\nconst out = [];\nfor (let i = 0; i < items.length; i++) {\n const r = items[i].json || {};\n const callId = r.id || (r.call && r.call.id) || '';\n const c = (src[i] && src[i].json) ? src[i].json : {};\n out.push({ json: { contactId: c.contactId, meetingId: c.meetingId, contactName: c.contactName, vapiCallId: callId } });\n}\nreturn out;\n"
},
"id": "e6efce3d-3bef-401d-a6c1-e6ab5ad4a9ae",
"name": "Extract Call",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1712,
2304
]
},
{
"parameters": {
"jsCode": "const cfg = $('Config').item.json;\nconst out = [];\nfor (const item of items) {\n const m = item.json;\n const c = Array.isArray(m.contacts) ? (m.contacts[0]||{}) : (m.contacts||{});\n const s = c && (Array.isArray(c.schools) ? (c.schools[0]||{}) : (c.schools||{})) || {};\n const when = m.scheduled_at ? new Date(m.scheduled_at).toUTCString() : 'your upcoming call';\n const vapiBody = {\n assistantId: cfg.vapiAssistantId,\n phoneNumberId: cfg.vapiPhoneNumberId,\n customer: { number: cfg.testNumber },\n assistantOverrides: {\n variableValues: {\n contactName: c.name || 'there',\n schoolName: s.name || 'your school',\n meetingTime: when\n }\n }\n };\n out.push({ json: { contactId: m.contact_id, meetingId: m.id, contactName: c.name, vapiBody: vapiBody } });\n}\nreturn out;\n"
},
"id": "f55ce8bc-1b89-418d-8444-666bd85048d5",
"name": "Build Call",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1232,
2304
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.vapi.ai/call",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.vapiBody }}",
"options": {}
},
"id": "a8c39fe4-e884-451c-9d5e-01f9588379df",
"name": "Trigger Vapi Call",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1472,
2304
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/meetings?status=eq.booked&select=id,scheduled_at,contact_id,contacts(name,schools(name))' }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"options": {}
},
"id": "153c6449-bd48-402d-a2b2-1fcbb1415169",
"name": "Get Booked Meetings",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1008,
2304
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {},
"id": "b62958c0-1eaa-4a46-9666-aeb6689edef1",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
564,
2304
]
}
],
"connections": {
"Get Booked Meetings": {
"main": [
[
{
"node": "Build Call",
"type": "main",
"index": 0
}
]
]
},
"Build Call": {
"main": [
[
{
"node": "Trigger Vapi Call",
"type": "main",
"index": 0
}
]
]
},
"Trigger Vapi Call": {
"main": [
[
{
"node": "Extract Call",
"type": "main",
"index": 0
}
]
]
},
"Extract Call": {
"main": [
[
{
"node": "Log Call",
"type": "main",
"index": 0
},
{
"node": "Set Meeting Confirmed",
"type": "main",
"index": 0
}
]
]
},
"Config": {
"main": [
[
{
"node": "Get Booked Meetings",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger": {
"main": [
[
{
"node": "Config",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
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.
httpHeaderAuthsupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Phase 5 - Confirmation Call (Vapi). Uses httpRequest. Event-driven trigger; 8 nodes.
Source: https://github.com/Tusharvijaypatil/LWL-Growth-Engine/blob/main/Workflows/phase6_confirmation_call.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.
This workflow listens for an “Approved” label on a Trello card, reads the AI draft bookkeeping JSON from card comments, and posts the corresponding transaction to Xero. It then adds a Xero deep link b
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t
[n8n] Advanced URL Parsing and Shortening Workflow - Switchy.io Integration. Uses splitInBatches, stickyNote, httpRequest, html. Event-driven trigger; 56 nodes.
[](https://youtu.be/c7yCZhmMjtI)