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
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 →