This workflow follows the Agent → Documentdefaultdataloader 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": "={{ $('Chat').item.json.body.chatInput }}",
"options": {
"systemMessage": "Voc\u00ea \u00e9 um assistente virtual prestativo. IMPORTANTE: Nunca inclua no seu texto ou na mem\u00f3ria de chat os logs internos de chamadas de ferramentas, IDs de execu\u00e7\u00e3o ou frases como 'Calling Azure AI Search'. Retorne \u00fanica e exclusivamente a resposta final formatada, amig\u00e1vel e direta para o usu\u00e1rio humano.",
"passthroughBinaryImages": true
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
1728,
896
],
"id": "97f28b37-3ec9-46f1-a56e-9667d3c0eaea",
"name": "AI Agent",
"executeOnce": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "624b3b7a-6a7c-423e-9e60-418969715811",
"name": "output",
"value": "={{ $('AI Agent').item.json.output }}",
"type": "string"
},
{
"id": "286c4d90-fe38-40ef-90e7-f60310b7fdf2",
"name": "thread_id",
"value": "={{ $json.id }}",
"type": "string"
},
{
"id": "61132193-d114-446c-95fb-20d410e9c464",
"name": "created_at",
"value": "={{ $json.created_at }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2288,
896
],
"id": "63dd5d20-0916-44d5-a2b5-a6059fd8837d",
"name": "Edit Fields"
},
{
"parameters": {
"httpMethod": "POST",
"path": "/llmchat/chat",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*"
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
928
],
"id": "88a47026-d241-4bca-8fbc-cdd3f80163bf",
"name": "Chat"
},
{
"parameters": {
"httpMethod": "POST",
"path": "/llmchat/auth",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
352
],
"id": "692204e4-1103-4c09-9f85-f001e02264ce",
"name": "Auth"
},
{
"parameters": {
"httpMethod": "POST",
"path": "llmchat/auth/validate-token",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
656
],
"id": "bdde5e3d-e495-4dee-9e74-e3f3edfb7e60",
"name": "Valida Token",
"executeOnce": true
},
{
"parameters": {
"jsCode": "// Pega o header Authorization enviado pelo React\nconst authHeader = $input.first().json.headers.authorization;\n\nif (!authHeader) {\n throw new Error('Header Authorization n\u00e3o encontrado');\n}\n\n// Remove o prefixo \"Bearer \" para pegar s\u00f3 o hash\nconst tokenRecebido = authHeader.replace('Bearer ', '').trim();\n\ntry {\n // 1. Decodifica Base64 para String (Simulando a verifica\u00e7\u00e3o de assinatura)\n // No Node.js do n8n, usamos Buffer em vez de atob()\n const jsonString = Buffer.from(tokenRecebido, 'base64').toString('utf-8');\n \n // 2. Transforma em Objeto JSON\n const payload = JSON.parse(jsonString);\n\n // 3. Verifica Expira\u00e7\u00e3o (Timestamp atual em segundos vs payload.exp)\n const agora = Math.floor(Date.now() / 1000);\n \n if (payload.exp < agora) {\n // Se o token venceu, retornamos valid: false\n return {\n json: {\n valid: false,\n message: 'Token expirado'\n }\n };\n }\n\n // 4. Se chegou aqui, \u00e9 v\u00e1lido! Retornamos os dados do usu\u00e1rio para o front\n return {\n json: {\n valid: true,\n user: {\n //id: payload.sub,\n email: payload.email,\n name: payload.name\n }\n }\n };\n\n} catch (error) {\n // Se o token for lixo ou mal formatado, cai aqui\n return {\n json: {\n valid: false,\n message: 'Token inv\u00e1lido ou mal formatado'\n }\n };\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
656
],
"id": "889c85a0-6fd5-45e8-81cb-d6ff05576a7a",
"name": "Verifica Token"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={\n \"valid\": \"{{ $json.valid }}\"\n}",
"options": {
"responseCode": "={{ $json.valid ? 200 : 401 }}"
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
816,
656
],
"id": "b30323aa-33ce-46b0-842e-92b7f45c136b",
"name": "Retorna Validacao"
},
{
"parameters": {
"options": {
"groupMessages": true
}
},
"type": "@n8n/n8n-nodes-langchain.memoryManager",
"typeVersion": 1.1,
"position": [
608,
1808
],
"id": "e1f69393-9c89-4c56-98e9-7f1fed2931fc",
"name": "Chat Memory Manager"
},
{
"parameters": {
"action": "generate",
"dataPropertyName": "=thread_id"
},
"type": "n8n-nodes-base.crypto",
"typeVersion": 1,
"position": [
1456,
992
],
"id": "3a0c2651-a0c9-43bb-a991-14486b7f2663",
"name": "Crypto"
},
{
"parameters": {
"jsCode": "// Acessa o primeiro item de entrada para extrair headers e dados bin\u00e1rios\nconst inputItem = $input.first();\nconst authHeader = inputItem.json.headers.authorization;\n\nif (!authHeader) {\n throw new Error('Header Authorization n\u00e3o encontrado');\n}\n\n// Remove o prefixo \"Bearer \" para pegar s\u00f3 o hash\nconst tokenRecebido = authHeader.replace('Bearer ', '').trim();\n\ntry {\n // ... Sua l\u00f3gica de decodifica\u00e7\u00e3o e valida\u00e7\u00e3o do token ...\n const jsonString = Buffer.from(tokenRecebido, 'base64').toString('utf-8');\n const payload = JSON.parse(jsonString);\n const agora = Math.floor(Date.now() / 1000);\n\n let outputJson;\n\n if (payload.exp < agora) {\n outputJson = {\n valid: false,\n message: 'Token expirado'\n };\n } else {\n outputJson = {\n valid: true,\n user: {\n id: payload.sub,\n email: payload.email,\n name: payload.name\n }\n };\n }\n\n // --- PARTE CRUCIAL PARA BIN\u00c1RIOS ---\n // Cria o objeto de retorno.\n const outputItem = {\n json: outputJson\n };\n\n // Se o item de entrada possu\u00eda dados bin\u00e1rios, anexe-os ao item de sa\u00edda.\n if (inputItem.binary) {\n outputItem.binary = inputItem.binary;\n }\n // ------------------------------------\n\n return [outputItem]; // Retorna um array contendo o item modificado\n\n} catch (error) {\n // ... Sua l\u00f3gica de erro ...\n const outputItem = {\n json: {\n valid: false,\n message: 'Token inv\u00e1lido ou mal formatado'\n }\n };\n \n // No caso de erro, voc\u00ea tamb\u00e9m pode optar por propagar o bin\u00e1rio ou n\u00e3o.\n // Se quiser propagar, adicione a mesma l\u00f3gica de 'if (inputItem.binary)'.\n\n return [outputItem];\n}\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
928
],
"id": "19b943e5-4761-4ef8-8137-d2b0970b2c56",
"name": "Verifica Token1"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "6b50c317-ce4b-475d-aef4-504c77d8153c",
"leftValue": "={{ $json.valid }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
816,
928
],
"id": "eb065d25-1ea2-41df-ab60-8dfa4ac2a404",
"name": "token v\u00e1lido?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "0e13063a-1e68-450b-b35b-658ba3beeae4",
"name": "user.id",
"value": "={{ $json.user.id }}",
"type": "string"
},
{
"id": "1caf328e-baa6-442b-8587-a6d38b34a8ce",
"name": "user.email",
"value": "={{ $json.user.email }}",
"type": "string"
},
{
"id": "f08d74ef-0160-4318-adde-3d6ec7a3a427",
"name": "user.name",
"value": "={{ $json.user.name }}",
"type": "string"
},
{
"id": "ba23d850-cb13-4dee-914a-ddf8d0ca2396",
"name": "body.chatInput",
"value": "={{ $('Chat').item.json.body.chatInput }}",
"type": "string"
},
{
"id": "188713f2-2c64-42d3-a0bb-52d86e07f373",
"name": "body.thread_id",
"value": "={{ $('Chat').item.json.body.thread_id }}",
"type": "string"
}
]
},
"includeOtherFields": true,
"options": {
"stripBinary": false
}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1024,
912
],
"id": "bbc9e4f1-10c2-4b68-8278-131ee762b3a6",
"name": "Informacoes"
},
{
"parameters": {
"jsCode": "// Pega o header Authorization enviado pelo React\nconst authHeader = $input.first().json.headers.authorization;\n\nif (!authHeader) {\n throw new Error('Header Authorization n\u00e3o encontrado');\n}\n\n// Remove o prefixo \"Bearer \" para pegar s\u00f3 o hash\nconst tokenRecebido = authHeader.replace('Bearer ', '').trim();\n\ntry {\n // 1. Decodifica Base64 para String (Simulando a verifica\u00e7\u00e3o de assinatura)\n // No Node.js do n8n, usamos Buffer em vez de atob()\n const jsonString = Buffer.from(tokenRecebido, 'base64').toString('utf-8');\n \n // 2. Transforma em Objeto JSON\n const payload = JSON.parse(jsonString);\n\n // 3. Verifica Expira\u00e7\u00e3o (Timestamp atual em segundos vs payload.exp)\n const agora = Math.floor(Date.now() / 1000);\n \n if (payload.exp < agora) {\n // Se o token venceu, retornamos valid: false\n return {\n json: {\n valid: false,\n message: 'Token expirado'\n }\n };\n }\n\n // 4. Se chegou aqui, \u00e9 v\u00e1lido! Retornamos os dados do usu\u00e1rio para o front\n return {\n json: {\n valid: true,\n user: {\n id: payload.sub,\n email: payload.email,\n name: payload.name\n }\n }\n };\n\n} catch (error) {\n // Se o token for lixo ou mal formatado, cai aqui\n return {\n json: {\n valid: false,\n message: 'Token inv\u00e1lido ou mal formatado'\n }\n };\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
592,
2288
],
"id": "2b8a8b45-2b44-45d1-b2e0-d0e582000d5c",
"name": "Verifica Token2"
},
{
"parameters": {
"aggregate": "aggregateAllItemData",
"include": "specifiedFields",
"fieldsToInclude": "id, name, created_at",
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
1008,
2288
],
"id": "cdc93ffc-3fdb-4451-aeca-ca6df1ab2d34",
"name": "Aggregate"
},
{
"parameters": {
"operation": "select",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "threads",
"mode": "list",
"cachedResultName": "threads"
},
"where": {
"values": [
{
"column": "user_id",
"value": "={{ $json.user.id }}"
}
]
},
"sort": {
"values": [
{
"column": "created_at",
"direction": "DESC"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
800,
2288
],
"id": "0692794f-5c41-46da-becf-c45397b19fe5",
"name": "Select rows from a table",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"path": "/llmchat/threads/:thread-id",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
1808
],
"id": "e5c0a31b-017f-46d6-b020-def6f998ceb8",
"name": "Obter thread"
},
{
"parameters": {
"path": "/llmchat/threads",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*"
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
384,
2288
],
"id": "33dc6389-aa7e-449d-bd08-4bfa5eb6837c",
"name": "Obter thread's"
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.params['thread-id'] }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
"typeVersion": 1.3,
"position": [
624,
2016
],
"id": "16f4de65-73d4-4be7-b2b1-46105e2fbfa2",
"name": "Postgres Chat Memory1",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "# Autentica\u00e7\u00e3o",
"height": 368,
"width": 1328
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
208
],
"typeVersion": 1,
"id": "79754682-b64f-4986-9fcf-4fafd09e2aa4",
"name": "Sticky Note"
},
{
"parameters": {
"content": "# Valida\u00e7\u00e3o",
"height": 240,
"width": 1328,
"color": 3
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
592
],
"typeVersion": 1,
"id": "d002e87e-5e66-4d8e-9be4-39e02c89abd3",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "# Chat",
"height": 400,
"width": 2320,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
848
],
"typeVersion": 1,
"id": "f15cc3ca-6f51-4e13-9103-40d9001f49a0",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "# Obter Thread",
"height": 432,
"width": 1136,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
1712
],
"typeVersion": 1,
"id": "71c942cd-bac8-429a-bdb0-c5798ba222cd",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "# Obter TODAS Threads do User",
"height": 352,
"width": 1136,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
2160
],
"typeVersion": 1,
"id": "56512777-b944-413a-baed-28da76a092c9",
"name": "Sticky Note4"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\n \"message\": \"sucesso!\"\n}",
"options": {
"responseCode": 200
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1024,
-64
],
"id": "73120c56-4de6-4f96-8efa-f46b021da80c",
"name": "Respond to Webhook5"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\n \"message\": \"Usu\u00e1rio n\u00e3o autenticado\"\n}",
"options": {
"responseCode": 401
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1024,
1056
],
"id": "f043e391-7224-4b87-a8f6-d47695bbb822",
"name": "Respond unauthenticated user"
},
{
"parameters": {
"model": "gpt-5-mini",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"typeVersion": 1,
"position": [
1600,
1104
],
"id": "0a951fa3-a840-4c9f-ad60-03140daa9c6f",
"name": "Azure OpenAI Chat Model1",
"credentials": {
"azureOpenAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "select",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "users",
"mode": "list",
"cachedResultName": "users"
},
"limit": 1,
"where": {
"values": [
{
"column": "email",
"value": "={{ $json.body.email }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
816,
352
],
"id": "aed301d0-6e49-4bb6-b571-b4a5295863bc",
"name": "Get a row",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "# Cadastro de usu\u00e1rio\n ",
"height": 352,
"width": 944,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
-160
],
"typeVersion": 1,
"id": "8d7456f2-4792-46a6-aaae-25815b5d9296",
"name": "Sticky Note5"
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "users",
"mode": "list",
"cachedResultName": "users"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"email": "={{ $json.body.email }}",
"password_hash": "={{ $json.password_hash }}",
"name": "={{ $json.body.name }}"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "email",
"displayName": "email",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "password_hash",
"displayName": "password_hash",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "name",
"displayName": "name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
816,
-64
],
"id": "2892f89c-ff9b-4e52-bb10-972f36ae7ab5",
"name": "Insert new user in \"users\" table",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"httpMethod": "POST",
"path": "/user/create",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*"
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
-64
],
"id": "f152f3e5-a0eb-47ff-9a45-1dabdc261c48",
"name": "New User Webhook"
},
{
"parameters": {
"type": "SHA3-256",
"value": "={{ $json.body.password }}",
"dataPropertyName": "password_hash"
},
"type": "n8n-nodes-base.crypto",
"typeVersion": 1,
"position": [
608,
352
],
"id": "36c85098-3c04-4383-8f17-ed9ad5bd846c",
"name": "Auth password hash"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\n \"message\": \"Usu\u00e1rio ou senha incorretos\"\n}",
"options": {
"responseCode": 401
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1232,
432
],
"id": "a063e6fe-cf5a-46b6-89bd-7b6a854b86f7",
"name": "Respond incorrect user"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={\n \"token\": \"{{ $json.token }}\",\n \"user\": {\n \"id\": \"{{ $('Get a row').item.json.id }}\",\n \"email\": \"{{ $('Get a row').item.json.email }}\", \n \"name\": \"{{ $('Get a row').item.json.name }}\"\n }\n}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1440,
272
],
"id": "02fa3a4d-b125-4ff7-af20-64267b514b2b",
"name": "Respond success to Auth"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "d6546914-8f14-4341-9a5b-3d1e3bc4501d",
"leftValue": "={{ $json.password_hash }}",
"rightValue": "={{ $('Auth password hash').item.json.password_hash }}",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1024,
352
],
"id": "4cf2cb09-aa8a-429b-bfd9-e6a27256f227",
"name": "User found?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "6653b41a-0d1e-4977-bd2b-736053231a6c",
"leftValue": "={{ $json.body.thread_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1232,
912
],
"id": "7dc13c9d-92ca-4292-bc00-c54a6ea6007c",
"name": "Have thread?"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1216,
2288
],
"id": "38fea260-f473-4d03-a5eb-799ed711b7e3",
"name": "Respond with thread list"
},
{
"parameters": {
"jsCode": "// Este \u00e9 um exemplo simplificado para a palestra.\n// Ele pega os dados do user e timestamp atual e codifica em Base64 para simular um token.\n\nconst payload = {\n sub: $input.first().json.id,\n email: $input.first().json.email,\n name: $input.first().json.name,\n exp: (Date.now() / 1000) + 7200 // Expira em 2 horas\n};\n\n// Codifica o payload JSON em uma string Base64 para simular o formato JWT\nconst tokenSimulado = btoa(JSON.stringify(payload));\n\n\nreturn [{ json: { token: tokenSimulado } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1232,
272
],
"id": "184d9965-92a5-47ff-8f02-165684617ea3",
"name": "Gera token de Auth"
},
{
"parameters": {
"type": "SHA3-256",
"value": "={{ $json.body.password }}",
"dataPropertyName": "password_hash"
},
"type": "n8n-nodes-base.crypto",
"typeVersion": 1,
"position": [
608,
-64
],
"id": "89d2b880-07f6-46f7-8a70-d3a763a47f04",
"name": "Encrypt password"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
2496,
896
],
"id": "2c5dc397-e0ca-4d77-be70-b612bd4b466c",
"name": "Chat response"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
944,
1808
],
"id": "59de7ae6-5c72-4326-9359-cecb8fae4c70",
"name": "Respond with thread messages"
},
{
"parameters": {
"mode": "insert",
"options": {
"clearIndex": false
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreAzureAISearch",
"typeVersion": 1.3,
"position": [
1296,
1360
],
"id": "d9134f8b-2949-4781-83da-7d5fe85cea44",
"name": "Azure AI Search Vector Store (insert)",
"credentials": {
"azureAiSearchApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "=**SEMPRE** use essa tool para consultar informa\u00e7\u00f5es que n\u00e3o souber exatamente.",
"includeDocumentMetadata": false,
"options": {
"queryType": "hybrid",
"filter": "=metadata/attributes/any(attr: attr/key eq 'user' and attr/value eq '{{ $('Informacoes').item.json.user.id }}')"
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreAzureAISearch",
"typeVersion": 1.3,
"position": [
1856,
1104
],
"id": "82f91855-3ec6-425d-9fb2-7109b4081ba9",
"name": "Azure AI Search Vector Store (Retrieve)",
"rewireOutputLogTo": "ai_tool",
"credentials": {
"azureAiSearchApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"dataType": "binary",
"binaryMode": "specificField",
"binaryDataKey": "file",
"options": {
"metadata": {
"metadataValues": [
{
"name": "user",
"value": "={{ $json.user.id }}"
}
]
}
}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
1344,
1552
],
"id": "10ebce81-8299-4c39-9b2d-e3e16bb7b5d5",
"name": "Default Data Loader"
},
{
"parameters": {
"model": "text-embedding-3-small",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsAzureOpenAi",
"typeVersion": 1,
"position": [
1856,
1472
],
"id": "b4b133d9-b2c9-4ab2-8f41-378cd76e852a",
"name": "Embeddings Azure OpenAI",
"credentials": {
"azureOpenAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Acessa o primeiro item de entrada para extrair headers e dados bin\u00e1rios\nconst inputItem = $input.first();\nconst authHeader = inputItem.json.headers.authorization;\n\nif (!authHeader) {\n throw new Error('Header Authorization n\u00e3o encontrado');\n}\n\n// Remove o prefixo \"Bearer \" para pegar s\u00f3 o hash\nconst tokenRecebido = authHeader.replace('Bearer ', '').trim();\n\ntry {\n // ... Sua l\u00f3gica de decodifica\u00e7\u00e3o e valida\u00e7\u00e3o do token ...\n const jsonString = Buffer.from(tokenRecebido, 'base64').toString('utf-8');\n const payload = JSON.parse(jsonString);\n const agora = Math.floor(Date.now() / 1000);\n\n let outputJson;\n\n if (payload.exp < agora) {\n outputJson = {\n valid: false,\n message: 'Token expirado'\n };\n } else {\n outputJson = {\n valid: true,\n user: {\n id: payload.sub,\n email: payload.email,\n name: payload.name\n }\n };\n }\n\n // --- PARTE CRUCIAL PARA BIN\u00c1RIOS ---\n // Cria o objeto de retorno.\n const outputItem = {\n json: outputJson\n };\n\n // Se o item de entrada possu\u00eda dados bin\u00e1rios, anexe-os ao item de sa\u00edda.\n if (inputItem.binary) {\n outputItem.binary = inputItem.binary;\n }\n // ------------------------------------\n\n return [outputItem]; // Retorna um array contendo o item modificado\n\n} catch (error) {\n // ... Sua l\u00f3gica de erro ...\n const outputItem = {\n json: {\n valid: false,\n message: 'Token inv\u00e1lido ou mal formatado'\n }\n };\n \n // No caso de erro, voc\u00ea tamb\u00e9m pode optar por propagar o bin\u00e1rio ou n\u00e3o.\n // Se quiser propagar, adicione a mesma l\u00f3gica de 'if (inputItem.binary)'.\n\n return [outputItem];\n}\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
1376
],
"id": "451de4fa-211b-4c8d-a776-6bf1710f3338",
"name": "Verifica Token3"
},
{
"parameters": {
"httpMethod": "POST",
"path": "/llmchat/rag/",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*"
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
1376
],
"id": "e524ef83-e1cf-4241-8f70-187b94b29c63",
"name": "Webhook1"
},
{
"parameters": {
"content": "# Inser\u00e7\u00e3o RAG\n",
"height": 432,
"width": 1776,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
1264
],
"typeVersion": 1,
"id": "b4dd257f-e3cb-4f92-a488-3644741257b4",
"name": "Sticky Note6"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "6b50c317-ce4b-475d-aef4-504c77d8153c",
"leftValue": "={{ $json.valid }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
816,
1376
],
"id": "f2e9d2ac-008e-4b91-8da0-0c75880fdabc",
"name": "token v\u00e1lido?1"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\n \"message\": \"Usu\u00e1rio n\u00e3o autenticado\"\n}",
"options": {
"responseCode": 401
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1024,
1520
],
"id": "98149bda-da77-4d05-80e4-1b9a484e0725",
"name": "Respond unauthenticated user1"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1648,
1360
],
"id": "d9da1831-4bcd-48d7-9828-ca46d07e57fa",
"name": "Upload response"
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "threads",
"mode": "list",
"cachedResultName": "threads"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"user_id": "={{ $('Informacoes').item.json.user.id }}",
"id": "={{ $('Informacoes').item.json.body.thread_id || $('Crypto').item.json.thread_id }}",
"name": "={{ $('Informacoes').item.json.body.chatInput }}"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "user_id",
"displayName": "user_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "name",
"displayName": "name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"skipOnConflict": true
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
2080,
896
],
"id": "3170baeb-b6d7-4452-8740-84c581076955",
"name": "add new thread in \"threads\" table",
"executeOnce": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.thread_id || $json.body.thread_id }}",
"contextWindowLength": 10
},
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
"typeVersion": 1.3,
"position": [
1744,
1104
],
"id": "8b46a209-ebd3-4b18-8eaf-8c9932cb44c1",
"name": "Postgres Chat Memory",
"executeOnce": false,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
}
],
"connections": {
"AI Agent": {
"main": [
[
{
"node": "add new thread in \"threads\" table",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Chat response",
"type": "main",
"index": 0
}
]
]
},
"Chat": {
"main": [
[
{
"node": "Verifica Token1",
"type": "main",
"index": 0
}
]
]
},
"Auth": {
"main": [
[
{
"node": "Auth password hash",
"type": "main",
"index": 0
}
]
]
},
"Valida Token": {
"main": [
[
{
"node": "Verifica Token",
"type": "main",
"index": 0
}
]
]
},
"Verifica Token": {
"main": [
[
{
"node": "Retorna Validacao",
"type": "main",
"index": 0
}
]
]
},
"Chat Memory Manager": {
"main": [
[
{
"node": "Respond with thread messages",
"type": "main",
"index": 0
}
]
]
},
"Crypto": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Verifica Token1": {
"main": [
[
{
"node": "token v\u00e1lido?",
"type": "main",
"index": 0
}
]
]
},
"token v\u00e1lido?": {
"main": [
[
{
"node": "Informacoes",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond unauthenticated user",
"type": "main",
"index": 0
}
]
]
},
"Informacoes": {
"main": [
[
{
"node": "Have thread?",
"type": "main",
"index": 0
}
]
]
},
"Verifica Token2": {
"main": [
[
{
"node": "Select rows from a table",
"type": "main",
"index": 0
}
]
]
},
"Aggregate": {
"main": [
[
{
"node": "Respond with thread list",
"type": "main",
"index": 0
}
]
]
},
"Select rows from a table": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"Obter thread": {
"main": [
[
{
"node": "Chat Memory Manager",
"type": "main",
"index": 0
}
]
]
},
"Obter thread's": {
"main": [
[
{
"node": "Verifica Token2",
"type": "main",
"index": 0
}
]
]
},
"Postgres Chat Memory1": {
"ai_memory": [
[
{
"node": "Chat Memory Manager",
"type": "ai_memory",
"index": 0
}
]
]
},
"Azure OpenAI Chat Model1": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get a row": {
"main": [
[
{
"node": "User found?",
"type": "main",
"index": 0
}
]
]
},
"Insert new user in \"users\" table": {
"main": [
[
{
"node": "Respond to Webhook5",
"type": "main",
"index": 0
}
]
]
},
"New User Webhook": {
"main": [
[
{
"node": "Encrypt password",
"type": "main",
"index": 0
}
]
]
},
"Auth password hash": {
"main": [
[
{
"node": "Get a row",
"type": "main",
"index": 0
}
]
]
},
"User found?": {
"main": [
[
{
"node": "Gera token de Auth",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond incorrect user",
"type": "main",
"index": 0
}
]
]
},
"Have thread?": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Crypto",
"type": "main",
"index": 0
}
]
]
},
"Gera token de Auth": {
"main": [
[
{
"node": "Respond success to Auth",
"type": "main",
"index": 0
}
]
]
},
"Encrypt password": {
"main": [
[
{
"node": "Insert new user in \"users\" table",
"type": "main",
"index": 0
}
]
]
},
"Azure AI Search Vector Store (insert)": {
"main": [
[
{
"node": "Upload response",
"type": "main",
"index": 0
}
]
]
},
"Azure AI Search Vector Store (Retrieve)": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Default Data Loader": {
"ai_document": [
[
{
"node": "Azure AI Search Vector Store (insert)",
"type": "ai_document",
"index": 0
}
]
]
},
"Embeddings Azure OpenAI": {
"ai_embedding": [
[
{
"node": "Azure AI Search Vector Store (Retrieve)",
"type": "ai_embedding",
"index": 0
},
{
"node": "Azure AI Search Vector Store (insert)",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Verifica Token3": {
"main": [
[
{
"node": "token v\u00e1lido?1",
"type": "main",
"index": 0
}
]
]
},
"Webhook1": {
"main": [
[
{
"node": "Verifica Token3",
"type": "main",
"index": 0
}
]
]
},
"token v\u00e1lido?1": {
"main": [
[
{
"node": "Azure AI Search Vector Store (insert)",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond unauthenticated user1",
"type": "main",
"index": 0
}
]
]
},
"add new thread in \"threads\" table": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Postgres Chat Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"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.
azureAiSearchApiazureOpenAiApipostgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Llm-Chat-Sqls. Uses agent, memoryManager, crypto, postgres. Webhook trigger; 52 nodes.
Source: https://github.com/talleswp/n8n-llm-chat-tester/blob/fd920d7bc9c440b56b2c399e6422840327bafd69/fluxo_n8n/llm-chat-SQLS.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.
Camila IA. Uses postgres, crypto, redis, agent. Webhook trigger; 92 nodes.
Hi! I’m Amanda, a creator of intelligent automations using n8n and Make. I’ve been building AI-powered workflows for over 2 years, always focused on usability and innovation. This one here is very spe
AI Multi-Document Analyzer with Smart Recommendations & Reporting
V3 Local Agentic RAG AI Agent. Uses documentDefaultDataLoader, memoryPostgresChat, chatTrigger, agent. Webhook trigger; 41 nodes.
Author: Jadai kongolo