This workflow follows the HTTP Request → Postgres 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": "Customer Embedding RAG Chat (Webhook-based)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "customer-embedding-chat",
"responseMode": "responseNode",
"options": {
"binaryPropertyName": "data"
}
},
"id": "webhook-trigger",
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
]
},
{
"parameters": {
"jsCode": "// Extract and validate chat input from webhook\nconst body = $json.body || $json;\nconst chatInput = body.chatInput || body.message;\nconst sessionId = body.sessionId || `session-${Date.now()}`;\nconst metadata = body.metadata || {};\n\n// Validate required fields\nif (!chatInput) {\n throw new Error('chatInput is required');\n}\n\nif (!metadata.customerId) {\n throw new Error('customerId is required in metadata');\n}\n\n// Extract customer information\nconst customerId = metadata.customerId;\nconst widgetId = metadata.widgetId || 'default';\nconst companyName = metadata.companyName || '';\n\n// Prepare for vector search\nreturn [{\n json: {\n chatInput,\n sessionId,\n customerId,\n widgetId,\n companyName,\n metadata,\n timestamp: new Date().toISOString(),\n // Chat context for webhook response\n isWebhookChat: true,\n responseFormat: 'json'\n }\n}];"
},
"id": "extract-webhook-data",
"name": "Extract Webhook Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/embeddings",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Authorization",
"value": "Bearer {{ $env.OPENAI_API_KEY }}"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-ada-002"
},
{
"name": "input",
"value": "={{ $json.chatInput }}"
}
]
},
"options": {
"timeout": 30000
}
},
"id": "generate-query-embedding",
"name": "Generate Query Embedding",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
680,
300
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT * FROM search_customer_embeddings(\n ARRAY{{ $json.data[0].embedding }}::vector(1536),\n '{{ $('Extract Webhook Data').item(0).json.customerId }}',\n 0.7,\n 5\n);",
"additionalFields": {}
},
"id": "vector-search",
"name": "Vector Search Documents",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [
900,
300
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Build RAG context from search results\nconst searchResults = $json;\nconst chatData = $('Extract Webhook Data').item(0).json;\nconst embeddingData = $('Generate Query Embedding').item(0).json;\n\n// Process search results\nlet contextText = '';\nlet sourceCount = 0;\nlet hasRelevantContent = false;\n\nif (searchResults && Array.isArray(searchResults) && searchResults.length > 0) {\n sourceCount = searchResults.length;\n hasRelevantContent = true;\n \n // Build context from search results\n const contexts = searchResults.map(result => {\n const similarity = Math.round((result.similarity || 0) * 100);\n return `[Kaynak ${similarity}% benzerlik]: ${result.content}`;\n });\n \n contextText = contexts.join('\\n\\n');\n} else {\n contextText = 'Belirli bir kaynak bulunamad\u0131. Genel bilgilerimle yan\u0131tlayaca\u011f\u0131m.';\n}\n\n// Prepare context for AI\nconst systemPrompt = `Sen ${chatData.companyName || '\u015firketin'} m\u00fc\u015fteri hizmetleri asistan\u0131s\u0131n. T\u00fcrk\u00e7e yan\u0131t ver.\n\nMevcut bilgiler:\n${contextText}\n\nKullan\u0131c\u0131 sorusu: ${chatData.chatInput}\n\nYan\u0131t\u0131n:\n- T\u00fcrk\u00e7e olmal\u0131\n- Dostane ve profesyonel ton kullan\n- Mevcut bilgilere dayal\u0131 yan\u0131t ver\n- E\u011fer bilgi yoksa, nazik\u00e7e belirt`;\n\nreturn [{\n json: {\n systemPrompt,\n userQuery: chatData.chatInput,\n contextText,\n hasRelevantContent,\n sourceCount,\n searchResultCount: sourceCount,\n customerId: chatData.customerId,\n widgetId: chatData.widgetId,\n sessionId: chatData.sessionId,\n timestamp: chatData.timestamp,\n tokensUsed: embeddingData.usage?.total_tokens || 0\n }\n}];"
},
"id": "build-rag-context",
"name": "Build RAG Context",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
300
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Authorization",
"value": "Bearer {{ $env.OPENAI_API_KEY }}"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "gpt-3.5-turbo"
},
{
"name": "messages",
"value": "={{ [{ \"role\": \"system\", \"content\": $json.systemPrompt }] }}"
},
{
"name": "max_tokens",
"value": 500
},
{
"name": "temperature",
"value": 0.7
}
]
},
"options": {
"timeout": 60000
}
},
"id": "generate-ai-response",
"name": "Generate AI Response",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1340,
300
]
},
{
"parameters": {
"jsCode": "// Format final webhook response\nconst aiResponse = $json;\nconst contextData = $('Build RAG Context').item(0).json;\nconst chatData = $('Extract Webhook Data').item(0).json;\n\n// Extract AI response text\nconst responseText = aiResponse.choices?.[0]?.message?.content || '\u00dczg\u00fcn\u00fcm, yan\u0131t olu\u015fturamad\u0131m.';\n\n// Generate follow-up prompts based on context\nconst followUpPrompts = [];\nif (contextData.hasRelevantContent) {\n followUpPrompts.push(\n 'Bu konuda daha detayl\u0131 bilgi alabilir miyim?',\n 'Ba\u015fka hangi konularda yard\u0131mc\u0131 olabilirsiniz?',\n 'Bu bilgiyle ilgili \u00f6rnek verebilir misiniz?'\n );\n} else {\n followUpPrompts.push(\n 'Ba\u015fka bir konuda yard\u0131m edebilir misiniz?',\n 'Size hangi konularda soru sorabilirim?',\n 'Daha spesifik bir soru sorabilir miyim?'\n );\n}\n\n// Prepare webhook response (ChatTrigger format compatible)\nconst webhookResponse = {\n output: responseText,\n followUpPrompts: followUpPrompts,\n metadata: {\n sessionId: chatData.sessionId,\n customerId: chatData.customerId,\n widgetId: chatData.widgetId,\n hasRelevantContent: contextData.hasRelevantContent,\n sourceCount: contextData.sourceCount,\n responseTime: Date.now() - new Date(chatData.timestamp).getTime(),\n tokensUsed: (contextData.tokensUsed || 0) + (aiResponse.usage?.total_tokens || 0),\n timestamp: new Date().toISOString(),\n // Webhook specific\n responseType: 'webhook-chat',\n success: true\n }\n};\n\nreturn [{ json: webhookResponse }];"
},
"id": "format-webhook-response",
"name": "Format Webhook Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1560,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"id": "webhook-response",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1780,
300
]
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Extract Webhook Data",
"type": "main",
"index": 0
}
]
]
},
"Extract Webhook Data": {
"main": [
[
{
"node": "Generate Query Embedding",
"type": "main",
"index": 0
}
]
]
},
"Generate Query Embedding": {
"main": [
[
{
"node": "Vector Search Documents",
"type": "main",
"index": 0
}
]
]
},
"Vector Search Documents": {
"main": [
[
{
"node": "Build RAG Context",
"type": "main",
"index": 0
}
]
]
},
"Build RAG Context": {
"main": [
[
{
"node": "Generate AI Response",
"type": "main",
"index": 0
}
]
]
},
"Generate AI Response": {
"main": [
[
{
"node": "Format Webhook Response",
"type": "main",
"index": 0
}
]
]
},
"Format Webhook Response": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true
},
"staticData": {},
"tags": [],
"triggerCount": 1,
"updatedAt": "2025-09-29T15:17:43.099Z",
"versionId": "1.0"
}
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.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Customer Embedding RAG Chat (Webhook-based). Uses httpRequest, postgres. Webhook trigger; 8 nodes.
Source: https://github.com/botfusions/n8n-rag-chatbot/blob/4d40b60fe6cc672aae3d91444e1dd19556703a2a/webhook-customer-embedding-chat.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.
Chatbot-Query-Qdrant. Uses httpRequest, postgres. Webhook trigger; 7 nodes.
Kreativ: RAG Ingestion Pipeline. Uses httpRequest, postgres. Webhook trigger; 5 nodes.
Camila IA. Uses postgres, crypto, redis, agent. Webhook trigger; 92 nodes.
HeyDinastia. Uses executeCommand, httpRequest, youTube, postgres. Webhook trigger; 66 nodes.
Corvus 3.2 Beta. Uses httpRequest, agent, lmChatOpenAi, vectorStoreSupabase. Webhook trigger; 48 nodes.