Bookkeeping Agent Output Problem

Hello, i have been working on a simple bookkeeping agent. The problem i am facing that its giving output on the end nodes but its not displaying it in the chat box saying [No response. Make sure the last executed node outputs the content to display here]

It only provides output when i ask simple questions like hi, how are you etc, but when i ask for a simple book keeping task it doesn’t provide the correct output response on the chatbox

my code is

</>

{
“name”: “Bookkeeping – Single Credit Sheet Debug”,
“nodes”: [
{
“parameters”: {
“options”: {}
},
“id”: “1”,
“name”: “When chat message received”,
“type”: “@n8n/n8n-nodes-langchain.chatTrigger”,
“typeVersion”: 1.1,
“position”: [250, 300],
“webhookId”: “bookkeeping-chat”
},
{
“parameters”: {
“sessionIdType”: “customKey”,
“sessionKey”: “={{ $json.sessionId }}”
},
“id”: “2”,
“name”: “Window Buffer Memory”,
“type”: “@n8n/n8n-nodes-langchain.memoryBufferWindow”,
“typeVersion”: 1.2,
“position”: [250, 500]
},
{
“parameters”: {
“method”: “POST”,
“url”: “https://api.openai.com/v1/chat/completions”,
“authentication”: “predefinedCredentialType”,
“nodeCredentialType”: “openAiApi”,
“sendHeaders”: true,
“headerParameters”: {
“parameters”: [
{
“name”: “Authorization”,
“value”: “=Bearer {{ $credentials.apiKey }}”
},
{
“name”: “Content-Type”,
“value”: “application/json”
}
]
},
“sendBody”: true,
“specifyBody”: “json”,
“jsonBody”: “={\n “model”: “gpt-4o-mini”,\n “temperature”: 0.2,\n “messages”: [\n {\n “role”: “system”,\n “content”: “You are a bookkeeping assistant for a personal finance chat agent. For now, the user has ONE Google Sheets tab called ‘Credit Payment’.\n\nYour job is to read the user’s message and decide if this is:\n- an ADD request (one or more expenses to record), or\n- a SUMMARY request (they want totals), or\n- a normal CHAT message.\n\nDETECTION RULES (VERY IMPORTANT):\n- If the message is only a greeting or small talk (examples: ‘hi’, ‘hello’, ‘hey’, ‘salam’, ‘assalamualaikum’, ‘what’s up’, ‘how are you’, ‘good morning’, etc.), you MUST return action: ‘chat’.\n- If the message mentions specific amounts of money together with verbs like ‘add’, ‘record’, ‘log’, ‘note’, ‘enter’, or ‘put’, you MUST treat it as an ADD request.\n- Only return action: ‘summary’ if the user EXPLICITLY asks for totals or summary, using phrases like: ‘how much’, ‘total’, ‘summary’, ‘show my spending’, ‘what have I spent’, ‘give me a breakdown’, etc.\n\nIf it is an ADD request:\n- Extract each expense with: description (string), amount (numeric), and date.\n- If date is not mentioned, set ‘date’ to null and let the workflow fill today’s date.\n- You MUST support multiple expenses in a single message.\n\nIf it is a SUMMARY request:\n- They are asking for things like: ‘how much have I spent’, ‘show my total credit spending’, ‘give me a summary of my expenses’, and similar.\n- You do NOT need to filter by month or year at the LLM level; just return action: ‘summary’.\n\nIf it is a regular CHAT message (greetings like ‘hi’, ‘salam’, or general questions not about adding or summarizing expenses):\n- Do NOT create expenses or summary instructions.\n- Just reply naturally as a friendly agent.\n\nReturn ONLY valid JSON in exactly one of these formats:\n\n1) For ADD requests (one or multiple expenses):\n{ \“action\”: \“add\”, \“expenses\”: [ { \“date\”: null OR \“YYYY-MM-DD\”, \“description\”: \“string\”, \“amount\”: 1234.5 } ] }\n\n2) For SUMMARY requests:\n{ \“action\”: \“summary\” }\n\n3) For regular CHAT messages:\n{ \“action\”: \“chat\”, \“message\”: \“string reply explaining or answering the user normally\” }\n\nIMPORTANT:\n- Return ONLY the JSON object.\n- No markdown.\n- No backticks.\n- No extra text.”\n },\n {\n “role”: “user”,\n “content”: “{{ $json.chatInput }}”\n }\n ]\n}”,
“options”: {}
},
“id”: “3”,
“name”: “Call OpenAI API”,
“type”: “n8n-nodes-base.httpRequest”,
“typeVersion”: 4.2,
“position”: [450, 300]
},
{
“parameters”: {
“jsCode”: “// Parse OpenAI API response into { action, … }\nconst response = $input.item.json;\nconst content = response.choices?.[0]?.message?.content || ‘’;\n\nlet parsedData;\ntry {\n const clean = content\n .replace(/json\\n?/gi, '')\n .replace(/\n?/gi, ‘’)\n .trim();\n parsedData = JSON.parse(clean);\n} catch (error) {\n // Fallback: treat as chat\n parsedData = {\n action: ‘chat’,\n message: content || ‘I can help you track and summarize your expenses.’\n };\n}\n\n// Normalise structures\nif (parsedData.action === ‘add’) {\n if (!Array.isArray(parsedData.expenses)) parsedData.expenses = ;\n} else if (parsedData.action === ‘summary’) {\n // nothing extra for now\n} else if (parsedData.action !== ‘chat’) {\n parsedData = {\n action: ‘chat’,\n message: content || ‘I can help you track and summarize your expenses.’\n };\n}\n\nreturn { json: parsedData };”
},
“id”: “4”,
“name”: “Parse AI Response”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [650, 300]
},
{
“parameters”: {
“conditions”: {
“string”: [
{
“value1”: “={{ $json.action }}”,
“value2”: “add”
}
]
}
},
“id”: “5”,
“name”: “IF Add”,
“type”: “n8n-nodes-base.if”,
“typeVersion”: 1,
“position”: [850, 250]
},
{
“parameters”: {
“conditions”: {
“string”: [
{
“value1”: “={{ $json.action }}”,
“value2”: “summary”
}
]
}
},
“id”: “6”,
“name”: “IF Summary”,
“type”: “n8n-nodes-base.if”,
“typeVersion”: 1,
“position”: [850, 450]
},
{
“parameters”: {
“jsCode”: “// Split expenses and fill missing date with today\nconst input = $input.item.json;\nconst expenses = input.expenses || ;\n\nconst today = new Date().toISOString().slice(0, 10);\n\nreturn expenses.map(e => ({\n json: {\n date: e.date || today,\n description: e.description || ‘’,\n amount: Number(e.amount) || 0\n }\n}));”
},
“id”: “7”,
“name”: “Split Expenses”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [1050, 200]
},
{
“parameters”: {
“operation”: “append”,
“documentId”: “YOUR_GOOGLE_SHEET_ID”,
“sheetName”: “Credit Payment”,
“columns”: {
“mappingMode”: “defineBelow”,
“value”: {
“Date”: “={{ $json.date }}”,
“Description”: “={{ $json.description }}”,
“Amount”: “={{ $json.amount }}”
}
},
“options”: {}
},
“id”: “8”,
“name”: “Sheets Credit (Append)”,
“type”: “n8n-nodes-base.googleSheets”,
“typeVersion”: 4,
“position”: [1250, 200],
“credentials”: {
“googleSheetsOAuth2Api”: {
“id”: “YOUR_CREDENTIAL_ID”,
“name”: “Google Sheets account”
}
}
},
{
“parameters”: {
“operation”: “getAll”,
“documentId”: “YOUR_GOOGLE_SHEET_ID”,
“sheetName”: “Credit Payment”,
“options”: {}
},
“id”: “9”,
“name”: “Read Credit Sheet”,
“type”: “n8n-nodes-base.googleSheets”,
“typeVersion”: 4,
“position”: [1050, 550],
“credentials”: {
“googleSheetsOAuth2Api”: {
“id”: “YOUR_CREDENTIAL_ID”,
“name”: “Google Sheets account”
}
}
},
{
“parameters”: {
“jsCode”: “// Calculate total and this-month total from Credit Payment sheet\nconst allItems = $input.all();\n\nlet total = 0;\nlet count = 0;\nconst transactions = ;\n\nlet monthTotal = 0;\nlet monthCount = 0;\n\nconst now = new Date();\nconst curYear = now.getFullYear();\nconst curMonth = now.getMonth();\n\nallItems.forEach(item => {\n const row = item.json;\n const amount = row.Amount ?? row.amount;\n const description = row.Description ?? row.description ?? ‘Unknown’;\n const dateStr = row.Date ?? row.date ?? ‘N/A’;\n\n if (amount !== undefined && !isNaN(amount) && Number(amount) !== 0) {\n const numAmount = Number(amount);\n total += numAmount;\n count += 1;\n transactions.push({ date: dateStr, description, amount: numAmount });\n\n const d = new Date(dateStr);\n if (!isNaN(d)) {\n if (d.getFullYear() === curYear && d.getMonth() === curMonth) {\n monthTotal += numAmount;\n monthCount += 1;\n }\n }\n }\n});\n\nlet message;\nif (count === 0) {\n message = ‘You do not have any recorded credit expenses yet.’;\n} else {\n message = ‘:bar_chart: Credit Payment Summary\n\n’;\n message += ‘This month: ’ + monthTotal.toLocaleString() + ’ PKR (’ + monthCount + ’ transaction(s))\n’;\n message += ‘All time: ’ + total.toLocaleString() + ’ PKR (’ + count + ’ transaction(s))\n\n’;\n message += ‘Recent transactions:\n’;\n\n const recentTxns = transactions.slice(-5).reverse();\n recentTxns.forEach((txn, idx) => {\n message += (idx + 1) + ‘. ’ + txn.description + ’ - ’ + txn.amount.toLocaleString() + ’ PKR (’ + txn.date + ‘)\n’;\n });\n\n if (transactions.length > 5) {\n message += ‘\n… and ’ + (transactions.length - 5) + ’ more transaction(s)’;\n }\n}\n\nreturn { json: { output: message } };”
},
“id”: “10”,
“name”: “Calculate Summary”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [1250, 550]
},
{
“parameters”: {
“aggregate”: “aggregateAllItemData”,
“options”: {}
},
“id”: “11”,
“name”: “Aggregate Expenses”,
“type”: “n8n-nodes-base.aggregate”,
“typeVersion”: 1,
“position”: [1450, 200]
},
{
“parameters”: {
“assignments”: {
“assignments”: [
{
“id”: “output_field”,
“name”: “output”,
“value”: “={{ $json.data.map((item, idx) => (idx + 1) + ‘. ’ + (item.description || ‘No description’) + ’ — ’ + (item.amount || 0) + ’ PKR on ’ + (item.date || ‘No date’)).join(’\n’) }}”,
“type”: “string”
}
]
},
“options”: {}
},
“id”: “12”,
“name”: “Format Success Response”,
“type”: “n8n-nodes-base.set”,
“typeVersion”: 3.4,
“position”: [1650, 200]
},
{
“parameters”: {
“jsCode”: “// Pass through chat replies\nconst data = $input.item.json || {};\nconst action = data.action;\nconst message = data.message;\n\nif (action === ‘chat’) {\n return {\n json: {\n output: message || ‘I can help you track and summarize your expenses.’\n }\n };\n}\n\nreturn ;”
},
“id”: “13”,
“name”: “Chat Passthrough”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [850, 650]
}
],
“connections”: {
“When chat message received”: {
“main”: [
[
{
“node”: “Call OpenAI API”,
“type”: “main”,
“index”: 0
}
]
],
“ai_memory”: [
[
{
“node”: “Window Buffer Memory”,
“type”: “ai_memory”,
“index”: 0
}
]
]
},
“Call OpenAI API”: {
“main”: [
[
{
“node”: “Parse AI Response”,
“type”: “main”,
“index”: 0
}
]
]
},
“Parse AI Response”: {
“main”: [
[
{
“node”: “IF Add”,
“type”: “main”,
“index”: 0
},
{
“node”: “IF Summary”,
“type”: “main”,
“index”: 0
},
{
“node”: “Chat Passthrough”,
“type”: “main”,
“index”: 0
}
]
]
},
“IF Add”: {
“main”: [
[
{
“node”: “Split Expenses”,
“type”: “main”,
“index”: 0
}
],

]
},
“IF Summary”: {
“main”: [
[
{
“node”: “Read Credit Sheet”,
“type”: “main”,
“index”: 0
}
],

]
},
“Split Expenses”: {
“main”: [
[
{
“node”: “Sheets Credit (Append)”,
“type”: “main”,
“index”: 0
}
]
]
},
“Sheets Credit (Append)”: {
“main”: [
[
{
“node”: “Aggregate Expenses”,
“type”: “main”,
“index”: 0
}
]
]
},
“Read Credit Sheet”: {
“main”: [
[
{
“node”: “Calculate Summary”,
“type”: “main”,
“index”: 0
}
]
]
},
“Aggregate Expenses”: {
“main”: [
[
{
“node”: “Format Success Response”,
“type”: “main”,
“index”: 0
}
]
]
},
“Format Success Response”: {
“main”: [
[
{
“node”: “When chat message received”,
“type”: “main”,
“index”: 0
}
]
]
},
“Calculate Summary”: {
“main”: [
[
{
“node”: “When chat message received”,
“type”: “main”,
“index”: 0
}
]
]
},
“Chat Passthrough”: {
“main”: [
[
{
“node”: “When chat message received”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“settings”: {},
“staticData”: null,
“tags”:
}

I had a bit of trouble reading the json of the workflow - after manually cleaning up it looks something like this?

It appears that you may want to first visit the Tutorial on building an AI workflow. This would be a good starting point because I can see that you’re trying to call the OpenAI API directly and connect it to a buffer but you’re not using the right nodes.

I can see that you want your chat bot to call out other branches, but actually that migh be better done by using a Workflow Tool - what this allows is your AI Agent to select which tool to use and run that workflow.

Here’s an example of how that would look like: