AutomationFlowsAI & RAG › Finance RAG — Retrieval Agent

Finance RAG — Retrieval Agent

Finance RAG — Retrieval Agent. Uses telegramTrigger, httpRequest, googleSheets, gmail. Event-driven trigger; 11 nodes.

Event trigger★★★★☆ complexity11 nodesTelegram TriggerHTTP RequestGoogle SheetsGmailTelegram
AI & RAG Trigger: Event Nodes: 11 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → Google Sheets 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
{
  "name": "Finance RAG \u2014 Retrieval Agent",
  "nodes": [
    {
      "id": "retrieval-001",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "retrieval-002",
      "name": "Embed Question",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        440,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'models/text-embedding-004', content: { parts: [{ text: $('Telegram Trigger').item.json.message.text }] } }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "retrieval-003",
      "name": "Query Pinecone",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        640,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://YOUR_PINECONE_INDEX_HOST/query",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Api-Key",
              "value": "YOUR_PINECONE_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ vector: $json.embedding.values, topK: 3, includeMetadata: true, namespace: 'finance-kb' }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "retrieval-004",
      "name": "Build RAG Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        840,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Pull Pinecone matches\nconst matches = $input.item.json.matches || [];\n\n// Build context from retrieved chunks\nconst context = matches.length > 0\n  ? matches.map((m, i) => `[Source ${i + 1}]\\n${m.metadata?.text || ''}\\n`).join('\\n')\n  : 'No relevant policy sections found in the knowledge base.';\n\n// Get original question and user info from Telegram trigger\nconst telegramMsg = $('Telegram Trigger').item.json.message;\nconst questionText = telegramMsg.text;\nconst username = telegramMsg.from?.username\n  ? `@${telegramMsg.from.username}`\n  : (telegramMsg.from?.first_name || 'Unknown User');\nconst chatId = telegramMsg.chat.id;\n\nconst prompt = `You are a Finance Policy Assistant for a company. Your job is to answer employee questions accurately using ONLY the company's finance policy documentation provided below.\n\nRULES:\n- Answer using only the context provided. Do not use outside knowledge.\n- If the answer is not in the context, respond with exactly: \"I don't know. This query isn't covered in the finance policy documentation \u2014 I'm escalating it to the finance team.\"\n- Keep answers concise, clear, and professional.\n- If there are specific numbers (limits, thresholds, days), state them explicitly.\n\nFINANCE POLICY CONTEXT:\n${context}\n\nEMPLOYEE QUESTION: ${questionText}\n\nANSWER:`;\n\nreturn [{\n  json: {\n    prompt,\n    questionText,\n    username,\n    chatId\n  }\n}];"
      }
    },
    {
      "id": "retrieval-005",
      "name": "Generate Answer",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1040,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ contents: [{ parts: [{ text: $json.prompt }] }], generationConfig: { temperature: 0.2, maxOutputTokens: 512 } }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "retrieval-006",
      "name": "Extract Answer",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1240,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract answer text from Gemini response\nconst candidates = $input.item.json.candidates || [];\nconst answerText = candidates[0]?.content?.parts?.[0]?.text\n  || \"I don't know. I couldn't retrieve an answer \u2014 escalating to the finance team.\";\n\n// Detect low-confidence or out-of-scope answers\nconst uncertaintyPhrases = [\n  \"i don't know\",\n  \"i do not know\",\n  \"not covered\",\n  \"not in the context\",\n  \"not found in\",\n  \"i'm escalating\",\n  \"escalating it to\",\n  \"cannot answer\",\n  \"no information\",\n  \"not available in\"\n];\nconst lower = answerText.toLowerCase();\nconst isUncertain = uncertaintyPhrases.some(phrase => lower.includes(phrase));\n\n// Pull context from upstream nodes\nconst buildPromptData = $('Build RAG Prompt').item.json;\n\nreturn [{\n  json: {\n    answerText: answerText.trim(),\n    isUncertain,\n    questionText: buildPromptData.questionText,\n    username: buildPromptData.username,\n    chatId: buildPromptData.chatId,\n    timestamp: new Date().toISOString()\n  }\n}];"
      }
    },
    {
      "id": "retrieval-007",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1440,
        300
      ],
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Audit Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Timestamp": "={{ $json.timestamp }}",
            "Username": "={{ $json.username }}",
            "Question": "={{ $json.questionText }}",
            "Answer": "={{ $json.answerText }}",
            "Escalated": "={{ $json.isUncertain ? 'YES' : 'NO' }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Timestamp",
              "displayName": "Timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Username",
              "displayName": "Username",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Question",
              "displayName": "Question",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "Answer",
              "displayName": "Answer",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "Escalated",
              "displayName": "Escalated",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ]
        },
        "options": {}
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "retrieval-008",
      "name": "Is Answer Uncertain?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1640,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "condition-uncertain",
              "leftValue": "={{ $json.isUncertain }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "retrieval-009",
      "name": "Gmail Escalation Alert",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1840,
        200
      ],
      "parameters": {
        "operation": "send",
        "toList": "YOUR_FINANCE_TEAM_EMAIL@company.com",
        "subject": "={{ '\u26a0\ufe0f Unresolved Finance Query from ' + $json.username }}",
        "message": "={{ '<p><strong>Finance Policy Bot \u2014 Escalation Alert</strong></p><p>A query came in via Telegram that the bot could not confidently answer from the knowledge base.</p><p><strong>User:</strong> ' + $json.username + '</p><p><strong>Question:</strong> ' + $json.questionText + '</p><p><strong>Bot Response:</strong> ' + $json.answerText + '</p><p><strong>Time:</strong> ' + $json.timestamp + '</p><p>Please respond to this employee directly.</p>' }}",
        "options": {
          "isHtml": true
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "retrieval-010",
      "name": "Telegram Reply \u2014 Escalated",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        2040,
        200
      ],
      "parameters": {
        "operation": "sendMessage",
        "chatId": "={{ $('Extract Answer').item.json.chatId }}",
        "text": "={{ '\u26a0\ufe0f ' + $('Extract Answer').item.json.answerText + '\\n\\n_You will hear back from the finance team shortly._' }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "retrieval-011",
      "name": "Telegram Reply \u2014 Answer",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1840,
        420
      ],
      "parameters": {
        "operation": "sendMessage",
        "chatId": "={{ $json.chatId }}",
        "text": "={{ '\ud83d\udccb *Finance Policy Answer*\\n\\n' + $json.answerText }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Embed Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embed Question": {
      "main": [
        [
          {
            "node": "Query Pinecone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Pinecone": {
      "main": [
        [
          {
            "node": "Build RAG Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build RAG Prompt": {
      "main": [
        [
          {
            "node": "Generate Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Answer": {
      "main": [
        [
          {
            "node": "Extract Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Answer": {
      "main": [
        [
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Google Sheets": {
      "main": [
        [
          {
            "node": "Is Answer Uncertain?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Answer Uncertain?": {
      "main": [
        [
          {
            "node": "Gmail Escalation Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram Reply \u2014 Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Escalation Alert": {
      "main": [
        [
          {
            "node": "Telegram Reply \u2014 Escalated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  }
}

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

About this workflow

Finance RAG — Retrieval Agent. Uses telegramTrigger, httpRequest, googleSheets, gmail. Event-driven trigger; 11 nodes.

Source: https://github.com/toxicbishop/Customer-Behavior-Analysis-Power-BI/blob/c10a1608e32696b5b92127dd68fa4bd96e5c0608/n8n/retrieval_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

Arvifund - Supabase (Fixed v5). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 91 nodes.

HTTP Request, Telegram, Google Sheets +9
AI & RAG

Arvifund - Supabase. Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.

HTTP Request, Telegram, Google Sheets +8
AI & RAG

Arvifund - Supabase (Fixed v2). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.

HTTP Request, Telegram, Google Sheets +9
AI & RAG

Arvifund - Supabase (Fixed v4). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.

HTTP Request, Telegram, Google Sheets +9
AI & RAG

Arvifund - Supabase (Fixed v3). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.

HTTP Request, Telegram, Google Sheets +9