This workflow follows the HTTP Request → OpenAI recipe pattern — see all workflows that pair these two integrations.
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 3 - Conversation Agent",
"nodes": [
{
"parameters": {
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/contacts?outreach_status=eq.replied&select=id,name,role,school_id,schools(name,tier,board,city),messages(direction,body,source,created_at)&messages.order=created_at.asc' }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"options": {}
},
"id": "03a4357c-a1a8-4e13-a31f-2fe03fbe12bb",
"name": "Get Replied + History",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1056,
1392
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const cfg = $('Config').item.json;\nconst CAL = cfg.calendlyUrl || 'https://calendly.com/your-team/intro-call';\n\n// Normalize: contacts may arrive as separate items or one array item\nlet rows = [];\nfor (const it of items) {\n const j = it.json;\n if (Array.isArray(j)) rows = rows.concat(j);\n else rows.push(j);\n}\n\nconst out = [];\nfor (const c of rows) {\n const s = Array.isArray(c.schools) ? (c.schools[0] || {}) : (c.schools || {});\n const msgs = Array.isArray(c.messages) ? c.messages.slice() : [];\n msgs.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));\n let transcript = '';\n for (const m of msgs) {\n const who = (m.direction === 'inbound') ? (c.name || 'Contact') : 'You (Learn with Leaders)';\n transcript += who + ': ' + ((m.body || '').trim()) + '\\n';\n }\n const userPrompt =\n \"You are continuing a LinkedIn conversation with \" + (c.name || 'the contact') +\n \", \" + (c.role || 'a school leader') + \" at \" + (s.name || 'their school') +\n \" (\" + (s.board || 'n/a') + \", \" + (s.city || 'n/a') + \").\\n\\n\" +\n \"Conversation so far:\\n\" + transcript + \"\\n\" +\n \"If they show ANY interest or ask a substantive question, warmly invite them to a quick 20-minute intro call and share this exact booking link: \" + CAL + \"\\n\\n\" +\n \"Write ONLY your next reply as Learn with Leaders.\";\n out.push({ json: { contactId: c.id, name: c.name, userPrompt: userPrompt } });\n}\nreturn out;\n"
},
"id": "09e28e8c-fdbb-4d60-a81c-3f3a44c666a7",
"name": "Build Chat",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1280,
1392
]
},
{
"parameters": {
"jsCode": "const src = $('Build Chat').all();\n\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}\n\nconst out = [];\nfor (let i = 0; i < items.length; i++) {\n const text = extractText(items[i].json).trim();\n const c = (src[i] && src[i].json) ? src[i].json : {};\n if (!text) continue;\n out.push({ json: { contactId: c.contactId, name: c.name, messageBody: text } });\n}\nreturn out;\n"
},
"id": "580240c3-7956-4437-b434-2ea02f8ab4de",
"name": "Extract Reply",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1984,
1392
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/messages' }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"contact_id\": $json.contactId, \"direction\": \"outbound\", \"body\": $json.messageBody, \"source\": \"ai\" } }}",
"options": {}
},
"id": "1e64d5f0-e157-42d6-aff9-c8293dbcd20f",
"name": "Insert Reply",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2224,
1280
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ca22ac9a-de4a-4118-a85a-ad65d97ebe19",
"name": "supabaseUrl",
"value": "https://YOUR_PROJECT_REF.supabase.co",
"type": "string"
},
{
"id": "0f5e9838-e88d-46b4-aa8a-9cb31405a5a2",
"name": "calendlyUrl",
"value": "https://calendly.com/your-team/intro-call",
"type": "string"
}
]
},
"options": {}
},
"id": "5363c0c5-969a-44a8-84ed-20de912039ae",
"name": "Config",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
832,
1392
]
},
{
"parameters": {
"method": "PATCH",
"url": "={{ $('Config').item.json.supabaseUrl + '/rest/v1/contacts?id=eq.' + $json.contactId }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"outreach_status\": \"in_conversation\", \"last_contacted_at\": $now.toISO() } }}",
"options": {}
},
"id": "ce191ff0-d669-491f-b58f-86ab5cbfc301",
"name": "Set In-Conversation",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2224,
1488
],
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4o-mini",
"mode": "list",
"cachedResultName": "GPT-4O-MINI"
},
"responses": {
"values": [
{
"role": "system",
"content": "You are the partnerships lead at Learn with Leaders (LwL), chatting with a K-12 school contact on LinkedIn.\n\nAbout LwL: LwL partners with schools worldwide to bring student leadership and enrichment programs \u2014 World Leaders Academy (global leadership & public speaking), Edge Club (career & entrepreneurship skills), Future Doctors Fellowship (medicine exposure & mentorship), and TEO Launchpad (teen entrepreneurship). Programs run with global mentors and are designed to slot alongside the school's existing curriculum, usually as after-school or enrichment cohorts. They build students' leadership, communication, and real-world skills and strengthen the school's profile.\n\nYour goals, in order:\n1. Sound like a real, warm person on LinkedIn \u2014 not a brochure. 2-5 sentences per reply.\n2. Introduce LwL and the single most relevant program for THIS school when it's natural.\n3. Answer their questions clearly: what it is, how it works, time/commitment, cost, and the student benefit. If you don't have an exact figure (e.g., precise pricing), say it depends on cohort size and format and that a quick call is the best way to tailor it \u2014 never invent specific prices or dates.\n4. Handle objections calmly (too busy, unsure it fits, need to check with the team): acknowledge, briefly reassure, lower the friction.\n5. DRIVE TOWARD A SHORT INTRO CALL \u2014 this is your main objective. As soon as the contact shows ANY interest or asks a substantive question, warmly invite them to a quick 20-minute call and share the booking link you're given. Don't wait for the perfect moment; propose the call early, gently, and more than once if needed.\n\nRules: Never invent facts about the school or specific prices. Don't be pushy or salesy. Exactly one clear next step per message. When you share the booking link, make it feel like a natural, low-pressure suggestion. Output ONLY your next reply \u2014 no role labels, no meta commentary."
},
{
"content": "={{ $json.userPrompt }}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.3,
"position": [
1568,
1408
],
"id": "33c2bb68-48f1-4fab-8839-461d369d0ed7",
"name": "Message a model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {},
"id": "aef1a1e5-ede8-499f-90cf-1096e9d0d3c7",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
612,
1392
]
}
],
"connections": {
"Get Replied + History": {
"main": [
[
{
"node": "Build Chat",
"type": "main",
"index": 0
}
]
]
},
"Build Chat": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Extract Reply": {
"main": [
[
{
"node": "Insert Reply",
"type": "main",
"index": 0
},
{
"node": "Set In-Conversation",
"type": "main",
"index": 0
}
]
]
},
"Config": {
"main": [
[
{
"node": "Get Replied + History",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Extract Reply",
"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.
openAiApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Phase 3 - Conversation Agent. Uses httpRequest, openAi. Event-driven trigger; 8 nodes.
Source: https://github.com/Tusharvijaypatil/LWL-Growth-Engine/blob/main/Workflows/phase4_conversation_agent.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.
🎯 Create viral TikToks, Shorts, Reels, podcasts, and ASMR videos in minutes — all on autopilot.
Generate AI viral videos with NanoBanana & VEO3, shared on socials via Blotato 2. Uses @blotato/n8n-nodes-blotato, googleSheets, lmChatOpenAi, toolThink. Event-driven trigger; 94 nodes.
How it Works
AI Agent Workflow. Uses telegramTrigger, chatTrigger, telegram, openAi. Event-driven trigger; 82 nodes.
The best content automation template in the market is now even better—with “deep research” on time-sensitive topics\! Unlike most n8n content automation templates that are mainly for “demo purposes,”