AutomationFlowsAI & RAG › AI Voice Chatbot with OpenAI

AI Voice Chatbot with OpenAI

Original n8n title: Simple N8n Workflow

Simple N8N Workflow. Uses agent, lmChatOpenAi, memoryBufferWindow, respondToWebhook. Webhook trigger; 14 nodes.

Webhook trigger★★★★☆ complexityAI-powered14 nodesAgentOpenAI ChatMemory Buffer WindowOpenAI
AI & RAG Trigger: Webhook Nodes: 14 Complexity: ★★★★☆ AI nodes: yes Added:

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 →

Download .json
{
  "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.

Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Aura-bot. Uses postgres, lmChatOpenAi, memoryBufferWindow, httpRequest. Webhook trigger; 82 nodes.

Postgres, OpenAI Chat, Memory Buffer Window +6
AI & RAG

My workflow 15. Uses httpRequest, memoryBufferWindow, agent, lmChatOpenAi. Webhook trigger; 74 nodes.

HTTP Request, Memory Buffer Window, Agent +5
AI & RAG

All-in-One Telegram/Baserow AI Assistant 🤖🧠 Voice/Photo/Save Notes/Long Term Mem. Uses stickyNote, lmChatOpenAi, telegram, agent. Webhook trigger; 48 nodes.

OpenAI Chat, Telegram, Agent +5
AI & RAG

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

OpenAI Chat, Telegram, Agent +5
AI & RAG

All-in-One Telegram/Baserow AI Assistant 🤖🧠 Voice/Photo/Save Notes/Long Term Mem. Uses lmChatOpenAi, telegram, agent, openAi. Webhook trigger; 48 nodes.

OpenAI Chat, Telegram, Agent +5