{
  "name": "AI Receptionist - Vapi Webhook",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "vapi-receptionist",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "3c193697-0c81-48cb-87eb-5926c8925862",
      "name": "Webhook (Vapi Tool Call)",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -560,
        272
      ]
    },
    {
      "parameters": {
        "jsCode": "// Vapi sends: { message: { toolCalls: [ { id, function: { name, arguments } } ] } }\nconst body = $input.first().json.body;\nconst call = body.message?.toolCalls?.[0] || body.toolCalls?.[0];\nif (!call) {\n  throw new Error('No toolCalls found in payload');\n}\n\nlet args = call.function.arguments;\nif (typeof args === 'string') {\n  try { args = JSON.parse(args); } catch (e) { args = {}; }\n}\n\nreturn [{\n  json: {\n    toolCallId: call.id,\n    toolName: call.function.name,\n    name: args.name || '',\n    phone: args.phone || '',\n    service: args.service || '',\n    date: args.date || '',\n    time: args.time || ''\n  }\n}];"
      },
      "id": "8f8cfb5c-f922-4eb4-a310-e3c94f263086",
      "name": "Parse Tool Call",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -336,
        272
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.toolName}}",
                    "rightValue": "check_availability",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.toolName}}",
                    "rightValue": "book_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.toolName}}",
                    "rightValue": "lookup_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.toolName}}",
                    "rightValue": "cancel_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "2f7ece4f-806c-408d-a580-75944a8a4e88",
      "name": "Route by Tool Name",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        -112,
        272
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "Appointments_Template",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 506913772,
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=506913772"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "date",
              "lookupValue": "={{$json.date}}"
            }
          ]
        },
        "options": {}
      },
      "id": "fe7b9d7e-0274-4869-94a4-bc55e45326be",
      "name": "Get Bookings For Date",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        128,
        0
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Compare fixed business slots vs already-booked rows for the date\nconst allSlots = ['09:00','10:00','11:00','12:00','14:00','15:00','16:00','17:00'];\nconst booked = $input.all().map(i => i.json.Time).filter(Boolean);\nconst free = allSlots.filter(s => !booked.includes(s));\n\nconst toolCallId = $('Parse Tool Call').first().json.toolCallId;\n\nreturn [{\n  json: {\n    results: [{\n      toolCallId,\n      result: free.length\n        ? `Available slots: ${free.join(', ')}`\n        : 'No slots available for that date, please pick another day.'\n    }]\n  }\n}];"
      },
      "id": "189ae8ce-be95-4b00-95b1-8ea83d3b7427",
      "name": "Build Availability Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        352,
        0
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "Appointments_Template",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 506913772,
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=506913772"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "name": "={{$json.name}}",
            "phone": "={{$json.phone}}",
            "service": "={{$json.service}}",
            "time": "={{$json.time}}",
            "date": "={{$json.date}}",
            "Status": "Booked"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "toolCallId",
              "displayName": "toolCallId",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "toolName",
              "displayName": "toolName",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "service",
              "displayName": "service",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "time",
              "displayName": "time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "date",
              "displayName": "date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "fcb3cb49-0010-41bc-baac-5f7b1e22eb1f",
      "name": "Append Booking Row",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        128,
        192
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const t = $('Parse Tool Call').first().json;\nreturn [{\n  json: {\n    results: [{\n      toolCallId: t.toolCallId,\n      result: `Booked ${t.service || 'appointment'} for ${t.name} on ${t.date} at ${t.time}. Confirmation sent.`\n    }]\n  }\n}];"
      },
      "id": "f8afe849-e48c-4493-bd3c-3c21ab1ca5c9",
      "name": "Build Booking Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        352,
        192
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "Appointments_Template",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 506913772,
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=506913772"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "phone",
              "lookupValue": "={{$json.phone}}"
            }
          ]
        },
        "options": {}
      },
      "id": "7f19ad8e-9b49-4530-a503-854161e47c1a",
      "name": "Find Appointments By Phone",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        128,
        368
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map(i => i.json).filter(r => r.Status !== 'Cancelled');\nconst toolCallId = $('Parse Tool Call').first().json.toolCallId;\n\nlet result;\nif (!rows.length) {\n  result = 'No upcoming appointment found for that number.';\n} else {\n  result = rows.map(r => `${r.Service} on ${r.Date} at ${r.Time} (Status: ${r.Status})`).join('; ');\n}\n\nreturn [{ json: { results: [{ toolCallId, result }] } }];"
      },
      "id": "cd00d015-a6d4-47fe-bcf6-e53b05f62c34",
      "name": "Build Lookup Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        352,
        368
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "Appointments_Template",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 506913772,
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=506913772"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Status": "Cancelled",
            "row_number": "={{$json.row_number}}"
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": [
            {
              "id": "toolCallId",
              "displayName": "toolCallId",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "toolName",
              "displayName": "toolName",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "service",
              "displayName": "service",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "time",
              "displayName": "time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "date",
              "displayName": "date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "8b42e7ba-d802-4ae8-a0ab-85aa7492aa61",
      "name": "Mark Row Cancelled",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        352,
        544
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const t = $('Parse Tool Call').first().json;\nconst matched = $input.all().length > 0;\nreturn [{\n  json: {\n    results: [{\n      toolCallId: t.toolCallId,\n      result: matched\n        ? `Your appointment on ${t.date} has been cancelled.`\n        : `I couldn't find an appointment on ${t.date} for that number.`\n    }]\n  }\n}];"
      },
      "id": "3550a85c-4f8a-4f55-8e3e-f505c233541f",
      "name": "Build Cancel Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        576,
        544
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{$json}}",
        "options": {}
      },
      "id": "da87ef7a-a2d5-4363-a56d-95049a7f0c6e",
      "name": "Respond to Vapi",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        800,
        272
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "Appointments_Template",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 506913772,
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=506913772"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "phone",
              "lookupValue": "={{$json.phone}}"
            },
            {
              "lookupColumn": "date",
              "lookupValue": "={{$json.date}}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        128,
        544
      ],
      "id": "480a0d9c-a946-4ed6-8284-7c84ad83af3d",
      "name": "Find Row To Cancel",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Webhook (Vapi Tool Call)": {
      "main": [
        [
          {
            "node": "Parse Tool Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Tool Call": {
      "main": [
        [
          {
            "node": "Route by Tool Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Tool Name": {
      "main": [
        [
          {
            "node": "Get Bookings For Date",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append Booking Row",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Appointments By Phone",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Row To Cancel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Bookings For Date": {
      "main": [
        [
          {
            "node": "Build Availability Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Availability Reply": {
      "main": [
        [
          {
            "node": "Respond to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Booking Row": {
      "main": [
        [
          {
            "node": "Build Booking Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Booking Reply": {
      "main": [
        [
          {
            "node": "Respond to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Appointments By Phone": {
      "main": [
        [
          {
            "node": "Build Lookup Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Lookup Reply": {
      "main": [
        [
          {
            "node": "Respond to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Row Cancelled": {
      "main": [
        [
          {
            "node": "Build Cancel Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Cancel Reply": {
      "main": [
        [
          {
            "node": "Respond to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Row To Cancel": {
      "main": [
        [
          {
            "node": "Mark Row Cancelled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "6d686090-5027-49f5-a4d3-6dc6691ae1ae",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "nodeGroups": [],
  "id": "ylnIeDHgtkqwVE0b",
  "tags": []
}