This workflow follows the Agent → OpenAI Chat 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 →
{
"nodes": [
{
"parameters": {
"promptType": "define",
"text": "={{ $json.body.query }}",
"options": {
"systemMessage": "={{ $json.body.system_prompt }}\n{{ $json.body && $json.body.structure != null ? \"You must return the final answer strictly as JSON that conforms to the following schema: \" + JSON.stringify($json.body.structure) : \"\" }}",
"passthroughBinaryImages": true,
"enableStreaming": "={{ $json.body.stream }}"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [
288,
-96
],
"id": "067cd63e-55be-4c7b-a233-bdd172de2dd5",
"name": "Agent"
},
{
"parameters": {
"httpMethod": "POST",
"path": "agent",
"responseMode": "streaming",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
32,
-96
],
"id": "d6cb88e7-5273-4f69-9291-85545e50d305",
"name": "Webhook"
},
{
"parameters": {
"jsCode": "return items.map(item => {\n if (item.json?.body?.voice) {\n item.json.body.voice = String(item.json.body.voice).toLowerCase();\n }\n return item;\n});\n"
},
"id": "39db000a-2584-4257-8b62-695f2c725bff",
"name": "Parse Voice",
"type": "n8n-nodes-base.code",
"position": [
160,
-272
],
"typeVersion": 1
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-5-mini",
"mode": "list",
"cachedResultName": "gpt-5-mini"
},
"options": {
"responseFormat": "={{ ($json.body && $json.body.structure != null) ? 'json_object' : 'text' }}"
}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
288,
80
],
"id": "08897615-add4-49dd-9f75-e066abe4ce91",
"name": "LLM",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.body.conversation_id }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [
384,
80
],
"id": "881a1f54-0edc-4b8f-b923-e03d8d34922a",
"name": "Memory"
},
{
"parameters": {
"respondWith": "binary",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
416,
-272
],
"id": "c57297cc-c125-4d9b-a12e-7010429cd695",
"name": "TTS Response"
},
{
"parameters": {
"httpMethod": "POST",
"path": "tts",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
32,
-272
],
"id": "f7a1a52d-2847-48a6-9a6b-9f76b02739fa",
"name": "Webhook TTS"
},
{
"parameters": {
"resource": "audio",
"input": "={{ $json.body.text }}",
"voice": "={{ $json.body.voice }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
288,
-272
],
"id": "3619c359-1c5e-410e-8649-07f8b166ca30",
"name": "TTS",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// n8n Code node (JavaScript) \u2014 Split binary_objects then merge into one item\n\nif (!items || items.length === 0) {\n return [];\n}\n\nconst originalJson = items[0].json ?? {};\nconst binaryList = Array.isArray(originalJson?.body?.binary_objects)\n ? originalJson.body.binary_objects\n : [];\n\n/** Remove any data:...;base64, prefix from a base64 string */\nfunction stripDataUrlPrefix(value) {\n return String(value ?? '').replace(/^data:.*?;base64,/, '');\n}\n\n/** Cross-platform basename */\nfunction getBaseName(pathValue) {\n const s = String(pathValue ?? '');\n const slash = s.split('/').pop();\n const backslash = s.split('\\\\').pop();\n return (slash && slash.length <= s.length ? slash : backslash) || '';\n}\n\n/** Sanitize a key; fallback to data<index> if empty */\nfunction makeSafeKey(name, index) {\n const cleaned = String(name ?? '')\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_\\-]/g, '_');\n return cleaned || `data${index}`;\n}\n\nconst mergedBinary = {};\n\n// If there are binaries, collect them all into a single `binary` object\nif (binaryList.length > 0) {\n for (let i = 0; i < binaryList.length; i++) {\n const binaryObject = binaryList[i];\n\n const desiredKey = makeSafeKey(binaryObject?.name, i);\n let finalKey = desiredKey;\n if (mergedBinary[finalKey]) {\n finalKey = `${finalKey}__${i}`; // avoid overwriting on collision\n }\n\n mergedBinary[finalKey] = {\n data: stripDataUrlPrefix(binaryObject?.data),\n fileName: getBaseName(binaryObject?.path) || desiredKey,\n mimeType: binaryObject?.mime_type || 'application/octet-stream',\n };\n }\n}\n\n// Match the merger node: keep first JSON and add _sources reflecting the (virtual) split count\nconst outputJson = {\n ...(originalJson ?? {}),\n _sources:\n binaryList.length > 0\n ? Array.from({ length: binaryList.length }, (_, i) => ({ index: i, ...(originalJson ?? {}) }))\n : [{ index: 0, ...(originalJson ?? {}) }],\n};\n\n// If no binaries existed, keep an empty `binary` object (merger node behavior)\nreturn [\n {\n json: outputJson,\n binary: mergedBinary,\n },\n];\n"
},
"id": "8930a0a0-57eb-4794-b409-2eb1bd8ba21f",
"name": "Attachments",
"type": "n8n-nodes-base.code",
"position": [
160,
-96
],
"typeVersion": 1
},
{
"parameters": {
"resource": "audio",
"operation": "transcribe",
"binaryPropertyName": "audio",
"options": {
"language": "={{ $json.body.language }}"
}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
288,
-448
],
"id": "e44f61fc-3454-4236-82c4-a16f804d1934",
"name": "STT",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
544,
-448
],
"id": "004e2ed1-7e40-4c33-8676-865b0150773e",
"name": "STT Response"
},
{
"parameters": {
"httpMethod": "POST",
"path": "stt",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
32,
-448
],
"id": "992a0376-59f9-459f-b6d5-c9d6964d09d1",
"name": "Webhook STT"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "09bd748c-5334-46ed-a9ed-a4f51c6d340a",
"name": "output",
"value": "={{ $json.text }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
416,
-448
],
"id": "ce888b04-8c32-45b7-8ef3-15da9c71ff41",
"name": "Extract Output"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// n8n Code node \u2014 Extract WebhookSTTRequestPayload.audio into binary\n\nconst audio = $json.body.audio;\n\nfunction stripDataUrlPrefix(value) {\n return String(value ?? '').replace(/^data:.*?;base64,/, '');\n}\n\nlet binary = {};\n\nif (audio && audio.data) {\n binary['audio'] = {\n data: stripDataUrlPrefix(audio.data), // base64 only\n fileName: audio.name || 'audio',\n mimeType: audio.mime_type || 'application/octet-stream',\n };\n}\n\nreturn {\n $json,\n binary,\n};\n"
},
"id": "56bae826-a297-4d86-9190-bfefdd7a3e2d",
"name": "Extract Audio",
"type": "n8n-nodes-base.code",
"position": [
160,
-448
],
"typeVersion": 1
}
],
"connections": {
"Agent": {
"main": [
[]
]
},
"Webhook": {
"main": [
[
{
"node": "Attachments",
"type": "main",
"index": 0
}
]
]
},
"Parse Voice": {
"main": [
[
{
"node": "TTS",
"type": "main",
"index": 0
}
]
]
},
"LLM": {
"ai_languageModel": [
[
{
"node": "Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Memory": {
"ai_memory": [
[
{
"node": "Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Webhook TTS": {
"main": [
[
{
"node": "Parse Voice",
"type": "main",
"index": 0
}
]
]
},
"TTS": {
"main": [
[
{
"node": "TTS Response",
"type": "main",
"index": 0
}
]
]
},
"Attachments": {
"main": [
[
{
"node": "Agent",
"type": "main",
"index": 0
}
]
]
},
"STT": {
"main": [
[
{
"node": "Extract Output",
"type": "main",
"index": 0
}
]
]
},
"STT Response": {
"main": [
[]
]
},
"Webhook STT": {
"main": [
[
{
"node": "Extract Audio",
"type": "main",
"index": 0
}
]
]
},
"Extract Output": {
"main": [
[
{
"node": "STT Response",
"type": "main",
"index": 0
}
]
]
},
"Extract Audio": {
"main": [
[
{
"node": "STT",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
}
}
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.
openAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
This workflow enables seamless voice-activated conversations by processing spoken inputs through a webhook trigger and delivering intelligent, spoken responses powered by OpenAI. It's ideal for developers building simple AI assistants, chatbots, or voice interfaces that require quick setup without complex infrastructure. The key step involves the AI agent analysing the parsed voice input with memory retention to generate context-aware replies, which are then converted to speech for natural interaction.
Use this workflow for prototyping voice-enabled apps or integrating basic AI chat into web services where low latency matters. Avoid it for high-volume production environments needing advanced scalability or custom security features. Common variations include swapping OpenAI for other LLM providers or adding nodes for database storage to extend conversation history beyond the buffer.
About this workflow
Simple N8N Workflow. Uses agent, lmChatOpenAi, memoryBufferWindow, respondToWebhook. Webhook trigger; 14 nodes.
Source: https://github.com/EuleMitKeule/webhook-conversation/blob/master/examples/simple_n8n_workflow.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.
Aura-bot. Uses postgres, lmChatOpenAi, memoryBufferWindow, httpRequest. Webhook trigger; 82 nodes.
My workflow 15. Uses httpRequest, memoryBufferWindow, agent, lmChatOpenAi. Webhook trigger; 74 nodes.
All-in-One Telegram/Baserow AI Assistant 🤖🧠 Voice/Photo/Save Notes/Long Term Mem. Uses stickyNote, lmChatOpenAi, telegram, agent. Webhook trigger; 48 nodes.
This n8n workflow transforms your Telegram bot into a powerful personal assistant that handles voice, photo, and text messages. The assistant uses AI to interpret messages, save important details as l
All-in-One Telegram/Baserow AI Assistant 🤖🧠 Voice/Photo/Save Notes/Long Term Mem. Uses lmChatOpenAi, telegram, agent, openAi. Webhook trigger; 48 nodes.