Thanks for your reply!
Because of the new-user link limit I cannot paste the full workflow with all nodes and URLs, so I’m sharing a minimal version that still shows the AI Agent setup, the Call Workflow Tool configuration, and the called “Terminfinder” sub-workflow.
Here is a reduced version of my Main workflow with just the AI Agent and the “Call Terminfinder” tool:
{
“nodes”: [
{
“id”: “930c1e83-98cd-4ef6-b16f-290341be57a3”,
“name”: “OpenAI Chat Model1”,
“type”: “@n8n/n8n-nodes-langchain.lmChatOpenAi”,
“typeVersion”: 1.2,
“position”: [112, 1792],
“parameters”: {
“model”: {
“_rl": true,
“value”: “gpt-4o”,
“mode”: “list”,
“cachedResultName”: “gpt-4o”
},
“options”: {}
}
},
{
“id”: “6fdf4b1b-4b8c-4482-80ae-37d5f7a2c701”,
“name”: “Simple Memory1”,
“type”: “@n8n/n8n-nodes-langchain.memoryBufferWindow”,
“typeVersion”: 1.3,
“position”: [256, 1792],
“parameters”: {
“sessionIdType”: “customKey”,
“sessionKey”: "={{ 'cid’ + $json.body.conversation.messages[0].conversation_id }}”,
“contextWindowLength”: 20
}
},
{
“id”: “ab957af6-965e-4a13-8ab2-b8432e90f2f5”,
“name”: “AI Agent1”,
“type”: “@n8n/n8n-nodes-langchain.agent”,
“typeVersion”: 2.2,
“position”: [496, 1472],
“onError”: “continueRegularOutput”,
“parameters”: {
“promptType”: “define”,
“text”: “={{ \n $json.body?.processed_message_content \n ?? $json.body?.content \n ?? $json.messages?.[0]?.content \n ?? $json.content \n ?? $json.text \n ?? $json.message \n ?? $json.transcription \n ?? ‘Hallo’\n}}\n”,
“options”: {
“systemMessage”: “=You are a helpful AI assistant.\n\n# Tools you can use\n\n1) Google Calendar\n - Read (get events)\n - Create (create event)\n - Delete (delete event)\n\n2) Google Sheets\n - Add Row\n - Update Row\n - Read\n\n3) Gmail\n - Send confirmation email to the user\n\n4) Terminfinder (Call n8n Workflow Tool)\n - Returns up to 5 truly free 60-minute time slots.\n - You never calculate appointment slots yourself.\n - Every time you call this tool, you send a JSON object as input.\n - Allowed top-level fields:\n - startAfter (ISO datetime string with offset)\n - weekday (1=Mon … 5=Fri)\n - If you don’t need any filter, send an empty object {}.\n - Never send these fields if they do not clearly follow from the user’s request.\n - Do not use a “query” object and do not send any other fields.\n - If an error happens when calling the tool: adjust the parameters and call the tool again before you reply.\n\nThe current date and time is German time: {{ $now }}.\n\n—\n\n# Conversation flow\n\nStart with:\n"How can I help you? Would you like to book an appointment?"\n\nIf the user says yes, ask for these in this order:\n1. Email address\n2. Name\n3. Phone number\n4. Location / time zone\n5. Reason for the appointment\n\nAlways ask only 1 question per message.\n\nAfter each piece of information, immediately save it to Google Sheets.\n\n—\n\n# Appointment finding (always via the Terminfinder tool)\n\nOnly when all 5 data points have been collected:\n\n1) First suggestion (default)\n- Call the tool with {} (do not set any fields)\n\n2) Weekday questions\n(e.g. “What do you have on Tuesday?”)\n- Detect the weekday → set weekday, leave startAfter undefined\n- Example:\n → { “weekday”: 2 }\n\n3) Later / more options\n(e.g. “Do you have something later?”)\n- Remember the end of the last slot (ISO datetime)\n- → { “startAfter”: “<ISO_DATETIME>” }\n\n4) Combined\n(e.g. “Later on Friday?”)\n- Example:\n → { “weekday”: 5, “startAfter”: “<ISO_DATETIME>” }\n\n5) No slots\n- Politely inform the user\n- Then call the tool again (for example only a later startAfter or {})\n\n6) Presentation of slots\n- Only use the labels from the tool\n\n—\n\n# Booking\n\n1. Check availability (Calendar Read)\n2. If free → Calendar Create (60 min)\n3. Update Google Sheet (Appointment Date & Status=confirmed)\n4. Send confirmation mail via Gmail\n5. Reply to the user with a clear confirmation, without suggesting new slots\n\n—\n\n# Style\n\n- Answer in German\n- Use formal “Sie”\n- Be polite and precise\n- Do not show technical terms or error messages\n”
}
}
},
{
“id”: “10d53a82-ab41-4189-9551-7c46c6f3f94b”,
“name”: “Call ‘Terminfinder’”,
“type”: “@n8n/n8n-nodes-langchain.toolWorkflow”,
“typeVersion”: 2.2,
“position”: [1440, 1792],
“parameters”: {
“description”: “Call this tool to calculate free 60-minute slots based on Google Calendar data.”,
“workflowId”: {
“__rl”: true,
“value”: “OIHkToPDdqOqjJXL”,
“mode”: “list”,
“cachedResultName”: “Terminfinder”
},
“workflowInputs”: {
“mappingMode”: “defineBelow”,
“value”: {},
“matchingColumns”: [
“query”
],
“schema”: ,
“attemptToConvertTypes”: false,
“convertFieldsToString”: false
}
}
}
],
“connections”: {
“OpenAI Chat Model1”: {
“ai_languageModel”: [
[
{
“node”: “AI Agent1”,
“type”: “ai_languageModel”,
“index”: 0
}
]
]
},
“Simple Memory1”: {
“ai_memory”: [
[
{
“node”: “AI Agent1”,
“type”: “ai_memory”,
“index”: 0
}
]
]
},
“Call ‘Terminfinder’”: {
“ai_tool”: [
[
{
“node”: “AI Agent1”,
“type”: “ai_tool”,
“index”: 0
}
]
]
}
}
}
And here is my sub-workflow “Terminfinder” that is being called:
{
“nodes”: [
{
“id”: “8352e342-9483-44c3-bae4-fb06814d653e”,
“name”: “When Executed by Another Workflow”,
“type”: “n8n-nodes-base.executeWorkflowTrigger”,
“typeVersion”: 1.1,
“position”: [0, 0],
“parameters”: {
“inputSource”: “passthrough”
}
},
{
“id”: “7cc52cca-a8f8-43d0-8814-ebc7ceace406”,
“name”: “Get many events”,
“type”: “n8n-nodes-base.googleCalendar”,
“typeVersion”: 1.3,
“position”: [208, 0],
“alwaysOutputData”: true,
“parameters”: {
“operation”: “getAll”,
“calendar”: {
“__rl”: true,
“value”: “[email protected]”,
“mode”: “list”,
“cachedResultName”: “[email protected]”
},
“limit”: 200,
“timeMax”: “={{ $now.plus({ days: 21 }) }}”,
“options”: {
“recurringEventHandling”: “expand”
}
}
},
{
“id”: “a5479adb-09d3-446a-b92d-d7a5d3cef42a”,
“name”: “Code in JavaScript”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [416, 0],
“parameters”: {
“mode”: “runOnceForAllItems”,
“jsCode”: “// INPUT: Events from “Get many events”\n// Optional INPUT from “When Executed by Another Workflow”:\n// - { startAfter: “…”, weekday: 3 }\n// - { query: { “weekday”: 5 } }\n// - { query: “{\“startAfter\”:\“2025-12-20T00:00:00+01:00\”,\“weekday\”:2}” }\n// - { query: “2025-12-19T00:00:00+01:00” } // interpreted as startAfter\n//\n// OUTPUT: Up to 5 free slots as items { start, end, label }\n\n// (full JS code as in my original post – logic for OFFICE_HOURS, LOOKAHEAD_DAYS, CLOSED_WORDS, parsing cfgRaw, handling startAfter and weekday,\n// building local datetimes with calendar offset, checking busy slots, collecting up to 5 free slots and returning them as [{ json: { start, end, label } }])”
}
}
],
“connections”: {
“When Executed by Another Workflow”: {
“main”: [
[
{
“node”: “Get many events”,
“type”: “main”,
“index”: 0
}
]
]
},
“Get many events”: {
“main”: [
[
{
“node”: “Code in JavaScript”,
“type”: “main”,
“index”: 0
}
]
]
}
}
}
The problem I am facing: When the user asks for specific days/times, the AI Agent should pass startAfter/weekday into the Call Terminfinder tool. I can see the input on the calling side, but inside the sub-workflow the data does not arrive in the Execute Workflow Trigger node (it looks empty), so my JS code falls back to the default behaviour and returns the first 5 free slots again.