This workflow follows the Agent → Google Gemini Chat 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 →
{
"updatedAt": "2025-12-11T16:44:19.000Z",
"createdAt": "2025-10-27T18:25:38.997Z",
"id": "eQO1bX8fadDwNvMu",
"name": "call_assi",
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {
"jsCode": "const phoneNumber = $input.first().json.body.message.call?.customer?.number ?? $input.first().json.body.message.toolCalls[0].function.arguments.phoneNumber\n \nreturn {phoneNumber}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
0,
0
],
"id": "495db161-4b96-492a-9ad0-86dc38369866",
"name": "Get phone number",
"disabled": true
},
{
"parameters": {
"description": "This tool's purpose is to cancel an appointment",
"workflowId": {
"__rl": true,
"value": "qJMnBdEthjId0vbd",
"mode": "list",
"cachedResultUrl": "/workflow/qJMnBdEthjId0vbd",
"cachedResultName": "cancel_appointment"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"appointmentId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('appointmentId', ``, 'string') }}"
},
"matchingColumns": [
"appointmentId"
],
"schema": [
{
"id": "appointmentId",
"displayName": "appointmentId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string",
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [
528,
416
],
"id": "f39f200d-f7a5-4115-8836-4fadf45f9b4c",
"name": "cancelAppointment"
},
{
"parameters": {
"description": "This tool's purpose is to reschedule the appointment for the user",
"workflowId": {
"__rl": true,
"value": "i4ywe4sK8CvhockA",
"mode": "list",
"cachedResultUrl": "/workflow/i4ywe4sK8CvhockA",
"cachedResultName": "reschedule_appointment"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"appointmentId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('appointmentId', ``, 'string') }}",
"appointmentDate": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('appointmentDate', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "appointmentId",
"displayName": "appointmentId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "appointmentDate",
"displayName": "appointmentDate",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [
688,
416
],
"id": "85f58800-5a87-4328-b9d4-1410e947aee6",
"name": "rescheduleAppointment"
},
{
"parameters": {
"description": "This tool will be responsible for booking a new appointment in the calendar",
"workflowId": {
"__rl": true,
"value": "RIvTGcIIx3zSsHiZ",
"mode": "list",
"cachedResultUrl": "/workflow/RIvTGcIIx3zSsHiZ",
"cachedResultName": "bookAppointment"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"appointmentStartTime": "={{ $json.body.message.toolCallList[0].function.arguments.preferredTime }}",
"firstName": "={{ $json.body.message.toolCallList[0].function.arguments.firstName }}",
"lastName": "={{ $json.body.message.toolCallList[0].function.arguments.lastName }}",
"phoneNumber": "={{ $json.body.message.toolCallList[0].function.arguments.phoneNumber }}"
},
"matchingColumns": [],
"schema": [
{
"id": "appointmentStartTime",
"displayName": "appointmentStartTime",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "firstName",
"displayName": "firstName",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "lastName",
"displayName": "lastName",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "phoneNumber",
"displayName": "phoneNumber",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "email",
"displayName": "email",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [
848,
416
],
"id": "9c11a3ce-7d89-46d1-af88-21ec1327ef29",
"name": "bookAppointment"
},
{
"parameters": {
"promptType": "define",
"text": "=You are assisting with appointment scheduling.\n\nInput:\n- toolCallName\n- preferredTime\n- firstName\n- lastName\n- appointmentId\n- appointmentDate\n\nOutput:\nReturn only JSON specifying what action to perform, using the provided arguments. No explanation.\n",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [
320,
192
],
"id": "b85bb6f9-2cbf-44e2-a765-cf5b507b1030",
"name": "AI Agent1",
"executeOnce": false
},
{
"parameters": {
"httpMethod": "POST",
"path": "e0eb2929-3f5d-48ea-ba3c-cec29f87010c",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-304,
176
],
"id": "cc500a08-b6fc-4f42-8664-3336608981b9",
"name": "Webhook1",
"executeOnce": false
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={\n \"results\": [\n {\n \"toolCallId\": \"{{ $('Webhook1').item.json.body.message.toolCalls[0].id }}\",\n \"result\": \"{{ $json.output.replaceAll(\"\\n\", \"\").replace(/\"([^\"]+)\"/g, '\\\\\"$1\\\\\"') }}\"\n }\n ]\n}\n",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
864,
176
],
"id": "4382f5fa-8815-4d95-a1e2-62f1a5e8d887",
"name": "Respond to Webhook"
},
{
"parameters": {
"description": "This tool will check for available timeslots and return their start time",
"workflowId": {
"__rl": true,
"value": "25L7BjNxaV3gQnx0",
"mode": "list",
"cachedResultUrl": "/workflow/25L7BjNxaV3gQnx0",
"cachedResultName": "getAvailability"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"startTime": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('startTime', ``, 'string') }}",
"endTime": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('endTime', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "startTime",
"displayName": "startTime",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "endTime",
"displayName": "endTime",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [
304,
400
],
"id": "b3923ed8-896b-4a17-840c-3d985fbab81c",
"name": "getAvailability"
},
{
"parameters": {
"description": "This tool will retrieve the existing appointment for the patient",
"workflowId": {
"__rl": true,
"value": "zZLxKOn5DyLfS8Ho",
"mode": "list",
"cachedResultUrl": "/workflow/zZLxKOn5DyLfS8Ho",
"cachedResultName": "getAppointment"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"appointmentId": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('appointmentId', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "appointmentId",
"displayName": "appointmentId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string",
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [
416,
400
],
"id": "a5281f99-3f80-4108-961b-c60ea8b1be9f",
"name": "getAppointment"
},
{
"parameters": {
"modelName": "models/gemini-2.0-flash-001",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
160,
416
],
"id": "dc85af03-53d2-44cc-bfec-dd05b8508b74",
"name": "Google Gemini Chat Model",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// n8n Function node \u2014 \"filter + map\" for Vapi \u2192 AI Agent integration\n// Returns a compact, safe object with all the template variables your prompt expects.\n//\n// Paste this into an n8n Function node. It expects the incoming webhook JSON at $json\n// and returns one item: item.json contains the mapped fields.\n\nconst body = $json[\"body\"] || {};\nconst message = body.message || {};\n\n// Helper: find the toolCall in multiple possible places\nconst toolCall =\n (message.toolCalls && message.toolCalls[0]) ||\n (message.toolCallList && message.toolCallList[0]) ||\n (message.toolWithToolCallList && message.toolWithToolCallList[0] && message.toolWithToolCallList[0].toolCall) ||\n null;\n\n// Safe parser for possibly stringified arguments\nfunction parseArgs(raw) {\n if (!raw) return {};\n if (typeof raw === \"object\") return raw;\n if (typeof raw === \"string\") {\n try {\n return JSON.parse(raw);\n } catch (e) {\n // fallback: simple key=value pairs or raw string\n return { rawString: raw };\n }\n }\n return {};\n}\n\n// Find last user message (best-effort)\nfunction findLastUserMessage(artifact, messageFallback) {\n if (!artifact) return messageFallback || \"\";\n const msgs = artifact.messagesOpenAIFormatted || artifact.messages || [];\n for (let i = msgs.length - 1; i >= 0; i--) {\n const m = msgs[i];\n if (!m) continue;\n // support both formats: {role, content} and {role, message}\n const role = (m.role || \"\").toString().toLowerCase();\n const text = m.content || m.message || \"\";\n if (role === \"user\" || role === \"human\") return text;\n // fallback heuristics: treat any item with content and not system as user\n if (text && role && role !== \"system\" && role !== \"assistant\") return text;\n }\n return messageFallback || \"\";\n}\n\nconst args = parseArgs(toolCall && toolCall.function && toolCall.function.arguments);\nconst toolName = (toolCall && ((toolCall.function && toolCall.function.name) || toolCall.name)) || null;\n\nconst artifact = message.artifact || {};\nconst lastUserMessage = findLastUserMessage(artifact, message.text || message.message || \"\");\n\n// Heuristics for name and phone if not provided explicitly\nlet firstName = args.firstName || null;\nlet lastName = args.lastName || null;\nlet fullName = args.name || args.fullName || null;\nlet phone = args.phoneNumber || args.mobile || args.phone || null;\n\nif (!fullName && lastUserMessage) {\n const nameMatch = lastUserMessage.match(/(?:for|booking for|new booking for|book)\\s+([A-Z][a-z]+(?:\\s[A-Z][a-z]+)*)/i);\n if (nameMatch) fullName = nameMatch[1];\n}\n\nif (!phone && lastUserMessage) {\n const phoneMatch = lastUserMessage.match(/(\\+?\\d[\\d\\-\\s]{7,}\\d)/);\n if (phoneMatch) phone = phoneMatch[0].replace(/\\s+/g, \"\");\n}\n\n// split fullName into first/last if needed\nif (!firstName && fullName) {\n const parts = fullName.trim().split(/\\s+/);\n firstName = parts[0] || null;\n lastName = parts.length > 1 ? parts.slice(1).join(\" \") : (lastName || null);\n}\n\n// Map all expected template variables (keep nulls if missing)\nconst mapped = {\n toolCallName: toolName,\n firstName: firstName || null,\n lastName: lastName || null,\n preferredTime: args.preferredTime || null,\n appointmentTime: args.appointmentTime || null,\n appointmentReason: args.appointmentReason || null,\n birthDate: args.birthDate || null,\n fullName: fullName || null,\n appointmentDate: args.appointmentDate || null,\n appointmentId: args.appointmentId || null,\n lastUserMessage,\n phoneNumber: phone || null,\n chatId: (body.chat && body.chat.id) || (body.chatId || null),\n // Debugging / telemetry (optional)\n _originalSizeBytes: Buffer.from(JSON.stringify($json)).length,\n _toolCallRawId: toolCall && toolCall.id ? toolCall.id : null,\n};\n\n// Return the compact mapped object\nreturn [{ json: mapped }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-16,
192
],
"id": "c50ae0f2-a39f-4378-8ea3-f2a6a0d457bf",
"name": "Code in JavaScript"
}
],
"connections": {
"cancelAppointment": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"rescheduleAppointment": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"bookAppointment": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"AI Agent1": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Webhook1": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"getAvailability": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"getAppointment": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "AI Agent1",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"timezone": "Asia/Kolkata",
"saveExecutionProgress": true,
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"staticData": null,
"meta": {
"templateCredsSetupCompleted": true
},
"versionId": "5541acbf-e935-40bd-afe2-3c68f9026c63",
"triggerCount": 1,
"shared": [
{
"updatedAt": "2025-10-27T18:25:39.013Z",
"createdAt": "2025-10-27T18:25:39.013Z",
"role": "workflow:owner",
"workflowId": "eQO1bX8fadDwNvMu",
"projectId": "U9gNtcfNPVWYu387"
}
],
"tags": []
}
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.
googlePalmApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
call_assi. Uses toolWorkflow, agent, lmChatGoogleGemini. Webhook trigger; 11 nodes.
Source: https://github.com/vignesh-potharaj/n8n/blob/330399d72a812fa7dadf18142cbd24ca02303fd1/workflows/call_assi.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.
Are you drowning in daily operational chaos, desperately trying to juggle sales, projects, content, and client communication? Imagine an AI brain that handles it all, freeing you to lead your business
CLINICAINTEGRAL_secretary. Uses postgres, mcpClientTool, googleDriveTool, toolWorkflow. Webhook trigger; 89 nodes.
Agente_DamaresEstetica. Uses postgres, agent, n8n-nodes-evolution-api, lmChatGoogleGemini. Webhook trigger; 87 nodes.
my-secretary. Uses postgres, mcpClientTool, googleDriveTool, toolWorkflow. Webhook trigger; 86 nodes.
secretaria. Uses postgres, n8n-nodes-evolution-api, openAi, httpRequest. Webhook trigger; 71 nodes.