AutomationFlowsData & Sheets › AI Receptionist

AI Receptionist

AI Receptionist. Uses googleCalendar, airtable. Webhook trigger; 59 nodes.

Webhook trigger★★★★★ complexity59 nodesGoogle CalendarAirtable
Data & Sheets Trigger: Webhook Nodes: 59 Complexity: ★★★★★ Added:

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": "AI Receptionist",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "call_results",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        1888
      ],
      "id": "b89601fa-ca76-40dc-93fa-82778f30105b",
      "name": "CALL_RESULTS"
    },
    {
      "parameters": {
        "jsCode": "const args = $json.body.message.toolCalls[0].function.arguments;\n\nreturn [{\n  starttime: args.starttime,\n  endtime: args.endtime\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        0
      ],
      "id": "a444ae9c-c8f7-4544-a1ab-5413674ac8da",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "22ad1235-389a-413f-9710-ba120e2629c1",
              "name": "timeZone",
              "value": "America/Chicago",
              "type": "string"
            },
            {
              "id": "a731de4a-6e28-419b-ae2c-f623d856e599",
              "name": "starttime",
              "value": "={{ $json.starttime }}",
              "type": "string"
            },
            {
              "id": "fd731b2f-2b08-499b-9781-dc81c4c9a386",
              "name": "body.message.toolCalls[0]",
              "value": "={{ $('Getslots').item.json.body.message.toolCalls[0].id }}",
              "type": "string"
            },
            {
              "id": "19bdc57b-aca5-49e4-9e7d-8e1294a011ad",
              "name": "customer.number",
              "value": "={{ $json.body.message.call.customer.number }}",
              "type": "string"
            },
            {
              "id": "dd2c144d-6fda-4971-bbd2-b3c779d96148",
              "name": "endtime",
              "value": "={{ $json.endtime }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        416,
        0
      ],
      "id": "0460107c-2c2a-44a5-ad47-5a85b8533f31",
      "name": "Input Arguments"
    },
    {
      "parameters": {
        "resource": "calendar",
        "calendar": {
          "__rl": true,
          "value": "justinrpandanan@gmail.com",
          "mode": "list",
          "cachedResultName": "justinrpandanan@gmail.com"
        },
        "timeMin": "={{ $json.starttime.toDateTime() }}",
        "timeMax": "={{ $json.endtime.toDateTime() || $now.plus(1, 'hour').toISO() }}",
        "options": {}
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        624,
        0
      ],
      "id": "b87409d6-f028-49d9-b6e6-414548c8a134",
      "name": "Check Availability",
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n    \"results\":[\n        {\n            \"toolCallId\":\"{{ $('Input Arguments').item.json.body.message.toolCalls[0] }}\",\n            \"result\":\"available:{{ $('Check Availability').item.json.available }}\"\n        }\n    ]\n}\n",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1040,
        -96
      ],
      "id": "f570d9d2-427b-4be2-a8fc-32f926666b9c",
      "name": "Response"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "getslots",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        0
      ],
      "id": "8ff22a12-e7dc-4493-bd79-c2c4c9e6c7b2",
      "name": "Getslots"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "d914c010-5297-4cb5-b24e-bbacc3743293",
              "leftValue": "={{ $json.available }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        832,
        0
      ],
      "id": "bfeb28f0-0bd6-474e-a7c9-d070ae9554bb",
      "name": "Check if time is available or not"
    },
    {
      "parameters": {
        "operation": "getAll",
        "calendar": {
          "__rl": true,
          "value": "justinrpandanan@gmail.com",
          "mode": "list",
          "cachedResultName": "justinrpandanan@gmail.com"
        },
        "timeMin": "={{ $now.toISO() }}",
        "timeMax": "={{ $now.plus(1, 'week').toISO() }}",
        "options": {
          "orderBy": "startTime"
        }
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        1040,
        96
      ],
      "id": "c7dd8669-49b9-480d-8040-4c599c0df6bc",
      "name": "Get all Calendar Events",
      "alwaysOutputData": true,
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e2226106-ec20-4af8-830b-3f7ec543502c",
              "name": "start",
              "value": "={{ DateTime.fromISO($json.start.dateTime).toLocaleString(DateTime.DATE_HUGE) }}, {{ DateTime.fromISO($json.start.dateTime).toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET) }}",
              "type": "string"
            },
            {
              "id": "777d20ec-c50e-48bb-ba9e-f543373edf18",
              "name": "end",
              "value": "={{ DateTime.fromISO($json.end.dateTime).toLocaleString(DateTime.DATE_HUGE) }}, {{ DateTime.fromISO($json.end.dateTime).toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET) }}",
              "type": "string"
            },
            {
              "id": "1bd7e131-8211-412d-8735-09f718077447",
              "name": "name",
              "value": "={{ $json.summary }}",
              "type": "string"
            },
            {
              "id": "a61e3190-2f42-4b3a-ba99-da9fec84af22",
              "name": "sort",
              "value": "={{ $json.start.dateTime }}",
              "type": "string"
            }
          ]
        },
        "options": {
          "ignoreConversionErrors": true
        }
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1248,
        96
      ],
      "id": "365c353d-4660-4eae-8962-9492e66f95f3",
      "name": "Extract start, end and name"
    },
    {
      "parameters": {
        "sortFieldsUi": {
          "sortField": [
            {
              "fieldName": "sort"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.sort",
      "typeVersion": 1,
      "position": [
        1456,
        96
      ],
      "id": "31c4541f-fffe-4bc1-a8bf-71052f64ea09",
      "name": "Sort"
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "response",
        "include": "allFieldsExcept",
        "fieldsToExclude": "sort",
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1664,
        96
      ],
      "id": "e8ee5966-123c-44db-af86-a495f5dc1fb8",
      "name": "Format Response"
    },
    {
      "parameters": {
        "jsCode": "// Input data\nconst inputData = $input.all()[0].json.response;\n\n// Define workday hours in CST\nconst WORKDAY_START = \"09:00:00 CST\";\nconst WORKDAY_END = \"18:00:00 CST\";\nconst SLOT_DURATION = 30 * 60 * 1000; // 30 minutes\n\n// Improved parser for the input format\nconst parseCST = (datetime) => {\n  if (!datetime) return null;\n  \n  // Clean the string: remove day name and convert timezone\n  let clean = datetime.replace(/^[A-Za-z]+,\\s*/, \"\"); // Remove \"Monday, \"\n  clean = clean.replace(\" GMT+8\", \"-08:00\").replace(\" CST\", \"-06:00\");\n  \n  const parsedDate = new Date(clean);\n  return isNaN(parsedDate.getTime()) ? null : parsedDate;\n};\n\n// Generate 30-min start times\nconst generateStartTimes = (start, end) => {\n  const startTimes = [];\n  let current = new Date(start);\n  while (current < end) {\n    startTimes.push(\n      current.toLocaleTimeString('en-US', {\n        timeZone: 'America/Chicago',\n        hour: 'numeric',\n        minute: '2-digit',\n        hour12: true\n      })\n    );\n    current = new Date(current.getTime() + SLOT_DURATION);\n  }\n  return startTimes;\n};\n\n// Find wide open ranges (3+ consecutive slots)\nconst findWideOpenRanges = (startTimes) => {\n  if (startTimes.length < 3) return [];\n  const ranges = [];\n  let rangeStart = null;\n  let consecutiveCount = 0;\n\n  for (let i = 0; i < startTimes.length - 1; i++) {\n    const currentTime = parseCST(`2000-01-01 ${startTimes[i]} CST`);\n    const nextTime = parseCST(`2000-01-01 ${startTimes[i + 1]} CST`);\n\n    if (currentTime && nextTime && (nextTime - currentTime) === SLOT_DURATION) {\n      if (rangeStart === null) rangeStart = startTimes[i];\n      consecutiveCount++;\n    } else {\n      if (consecutiveCount >= 2) {\n        ranges.push(`${rangeStart} to ${startTimes[i]}`);\n      }\n      rangeStart = null;\n      consecutiveCount = 0;\n    }\n  }\n\n  if (consecutiveCount >= 2) {\n    ranges.push(`${rangeStart} to ${startTimes[startTimes.length - 1]}`);\n  }\n  return ranges;\n};\n\n// Group meetings by date\nconst meetingsByDate = inputData.reduce((acc, meeting) => {\n  const start = parseCST(meeting.start);\n  const end = parseCST(meeting.end);\n  if (!start || !end) return acc;\n\n  const dateKey = start.toISOString().split('T')[0];\n  if (!acc[dateKey]) acc[dateKey] = [];\n  acc[dateKey].push({ start, end });\n  return acc;\n}, {});\n\n// Generate availability\nconst availability = Object.keys(meetingsByDate)\n  .filter(date => {\n    const dayOfWeek = new Date(date).getUTCDay();\n    return dayOfWeek !== 0 && dayOfWeek !== 6; // Skip weekends\n  })\n  .map(date => {\n    const workdayStart = parseCST(`${date} ${WORKDAY_START}`);\n    const workdayEnd = parseCST(`${date} ${WORKDAY_END}`);\n    const dayMeetings = meetingsByDate[date].sort((a, b) => a.start - b.start);\n\n    let availableStartTimes = [];\n    let lastEnd = workdayStart;\n\n    for (const meeting of dayMeetings) {\n      if (meeting.start > lastEnd) {\n        availableStartTimes = availableStartTimes.concat(generateStartTimes(lastEnd, meeting.start));\n      }\n      lastEnd = meeting.end > lastEnd ? meeting.end : lastEnd;\n    }\n\n    if (lastEnd < workdayEnd) {\n      availableStartTimes = availableStartTimes.concat(generateStartTimes(lastEnd, workdayEnd));\n    }\n\n    const wideOpenRanges = findWideOpenRanges(availableStartTimes);\n\n    return {\n      date: new Date(date).toLocaleDateString('en-US', {\n        weekday: 'long',\n        year: 'numeric',\n        month: 'long',\n        day: 'numeric'\n      }),\n      availableStartTimes,\n      wideOpenRanges\n    };\n  });\n\n// Format the final output\nconst availableTimes = availability\n  .map(({ date, availableStartTimes, wideOpenRanges }) => {\n    const timesList = availableStartTimes.length \n      ? availableStartTimes.map(t => `- ${t}`).join('\\n')\n      : \"None\";\n\n    const rangesText = wideOpenRanges.length\n      ? wideOpenRanges.map(r => `- ${r}`).join('\\n')\n      : \"None\";\n\n    return `### ${date}\\n\\n` +\n           `**Available Start Times:**\\n${timesList}\\n\\n` +\n           `**Wide Open Ranges:**\\n${rangesText}`;\n  })\n  .join('\\n\\n---\\n\\n');\n\nreturn {\n  json: {\n    availableTimes: availableTimes || \"No availability found for the given dates.\"\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1872,
        96
      ],
      "id": "9ffc7d00-cda5-4982-96c2-1a442b43fc21",
      "name": "Available Start Times & Ranges"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n    \"results\":[\n        {\n            \"toolCallId\":\"{{ $('Getslots').item.json.body.message.toolCalls[0].id }}\",\n            \"result\":\"The original time is not available, here are available slots:{{ $json.availableTimes.replace(/\\\\n/g, ' ').replace(/\\s*:\\s*/g, ':').replace(/\\s+/g, ' ').trim() }}\"\n        }\n    ]\n}\n",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        2080,
        96
      ],
      "id": "863a6353-2e6c-4e6e-b3a7-dc99cc9f2241",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "Bookslots",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        528
      ],
      "id": "767cc96d-af4f-4553-a3db-918bf8acc71d",
      "name": "BookSlots"
    },
    {
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  const toolCalls = item.json.body?.message?.toolCalls;\n  \n  if (!toolCalls || toolCalls.length === 0) {\n    results.push(item);\n    continue;\n  }\n\n  const toolCall = toolCalls[0];\n  let safe_arguments = {};\n\n  try {\n    // Parse the arguments string into a real object\n    safe_arguments = JSON.parse(toolCall.function.arguments || \"{}\");\n  } catch (e) {\n    console.error(\"Failed to parse arguments:\", e);\n    safe_arguments = toolCall.function.arguments; // fallback as string\n  }\n\n  results.push({\n    json: {\n      ...item.json,\n      toolCallId: toolCall.id,\n      functionName: toolCall.function.name,\n      safe_arguments: safe_arguments   // This bypasses the security restriction\n    }\n  });\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        192,
        528
      ],
      "id": "49109159-5615-45d7-965c-f3ef709e87ba",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cd9123fc-6a32-4f5a-b938-e78bc805a3ce",
              "name": "toolCallId",
              "value": "={{ $json.body.message.toolCallList[0].id }}",
              "type": "string"
            },
            {
              "id": "bae5a44d-991d-4a45-b40d-2fadbb9d0eb2",
              "name": "timeZone",
              "value": "America/Chicago",
              "type": "string"
            },
            {
              "id": "8ca64701-73a5-4fe0-8386-5c1795541e07",
              "name": "name",
              "value": "={{ $json.safe_arguments.name }}",
              "type": "string"
            },
            {
              "id": "f5399cc7-48b5-4bc7-95fc-dbde310f2281",
              "name": "email",
              "value": "={{ $json.safe_arguments.email || \"justinrpandanan@gmail.com\" }}",
              "type": "string"
            },
            {
              "id": "1c7d2e16-ca06-44ce-beb7-085a5e371d52",
              "name": "language",
              "value": "en",
              "type": "string"
            },
            {
              "id": "18fdea32-74f8-4b07-afe5-13023ae75425",
              "name": "notes",
              "value": "={{ $json.safe_arguments.notes }}",
              "type": "string"
            },
            {
              "id": "51dd1dc8-d15d-4cc1-b207-c3b045c466be",
              "name": "starttime",
              "value": "={{ $json.safe_arguments.starttime }}",
              "type": "string"
            },
            {
              "id": "27f45912-8316-4cd6-bfc4-35ed40696654",
              "name": "endtime",
              "value": "={{ $json.safe_arguments.endtime }}",
              "type": "string"
            },
            {
              "id": "aea82dff-92ab-4f09-bdb8-49891db98ba2",
              "name": "title",
              "value": "={{ $json.safe_arguments.Title }}",
              "type": "string"
            },
            {
              "id": "8bb78aa1-4622-425c-b02e-83d3b9926ac3",
              "name": "customer_number",
              "value": "={{ $json.body.message.call.customer_number || \"\" }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        416,
        528
      ],
      "id": "c43b7cbe-dbab-4766-b8a1-c1d1849568d3",
      "name": "input Arguments from booking tools"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "4daa95da-aebf-4e5e-a042-ade5e81993a8",
              "leftValue": "={{ ($json.email).isEmail() }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        624,
        528
      ],
      "id": "e17f7449-f621-480f-8205-4b349720e85d",
      "name": "has all information"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f5367afe-efc7-4e71-8c53-55452b12ffb3",
              "name": "results[0].toolCallId",
              "value": "={{ $('input Arguments from booking tools').item.json.toolCallId }}",
              "type": "string"
            },
            {
              "id": "482b917d-b6b5-487c-b5bf-d87549631ddf",
              "name": "results[0].result",
              "value": "You must provide an email. name and notse to call this tool",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        832,
        640
      ],
      "id": "bcc579b1-0f7c-4d1d-a812-40e710f5c033",
      "name": "Build Error Response Payload"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\n  \"message\": \"Booking failed!\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1040,
        640
      ],
      "id": "7f60fcbb-560d-4ea8-b27c-238ac439b363",
      "name": "Respond to Webhook1"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const escapeStringForJson = (str) => {\n    return str\n        .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n        .replace(/\"/g, '\\\\\"')    // Escape double quotes\n        .replace(/\\n/g, '\\\\n')   // Escape newlines\n        .replace(/\\r/g, '\\\\r')   // Escape carriage returns\n        .replace(/\\t/g, '\\\\t');  // Escape tabs\n};\n\n// Escape the notes field\n$input.item.json.notes = escapeStringForJson($input.item.json.notes);\n\nreturn $input.item;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        832,
        432
      ],
      "id": "855c7bba-b922-47b5-87b3-7dcdf467ae54",
      "name": "Escape json"
    },
    {
      "parameters": {
        "jsCode": "// Get all input items\nconst items = $input.all();\n\n// Loop through each item\nfor (const item of items) {\n  // Get the values from the current item's JSON data\n  const startTimeUTC = item.json.starttime;\n  const endTimeUTC = item.json.endtime;\n  const targetTimeZone = item.json.timeZone; // e.g., \"America/Chicago\"\n\n  // Basic validation: ensure the necessary fields exist\n  if (!startTimeUTC || !endTimeUTC || !targetTimeZone) {\n    console.warn(`Skipping item due to missing time data or timezone. Item JSON: ${JSON.stringify(item.json)}`);\n    item.json.conversionError = \"Missing starttime, endtime, or timeZone\";\n    continue; // Move to the next item\n  }\n\n  try {\n    // --- Start Time Conversion ---\n    // Parse the original UTC ISO string using Luxon (NO $ prefix)\n    const startDt = luxon.DateTime.fromISO(startTimeUTC, { zone: 'utc' });\n\n    // Convert the DateTime object to the target timezone\n    const startDtTargetZone = startDt.setZone(targetTimeZone);\n\n    // Check if the conversion was valid\n    if (!startDtTargetZone.isValid) {\n      throw new Error(`Failed to convert start time. Reason: ${startDtTargetZone.invalidReason || 'Unknown'}`);\n    }\n\n    // Format the result back into an ISO string with the correct offset\n    item.json.starttime = startDtTargetZone.toISO();\n\n    // --- End Time Conversion ---\n    // Parse the original UTC ISO string using Luxon (NO $ prefix)\n    const endDt = luxon.DateTime.fromISO(endTimeUTC, { zone: 'utc' });\n\n    // Convert the DateTime object to the target timezone\n    const endDtTargetZone = endDt.setZone(targetTimeZone);\n\n     // Check if the conversion was valid\n    if (!endDtTargetZone.isValid) {\n      throw new Error(`Failed to convert end time. Reason: ${endDtTargetZone.invalidReason || 'Unknown'}`);\n    }\n\n    // Format the result back into an ISO string with the correct offset\n    item.json.endtime = endDtTargetZone.toISO();\n\n    // Optionally remove the error flag if conversion was successful this time\n    delete item.json.conversionError;\n\n  } catch (error) {\n    console.error(`Error converting time for item: ${JSON.stringify(item.json)}. Error: ${error.message}`);\n    // Add/update the error flag to the item's JSON\n    item.json.conversionError = error.message;\n  }\n}\n// Return the modified array of items\nreturn items;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        432
      ],
      "id": "40346457-b312-4f59-83b7-6a4f202be0b9",
      "name": "Convert time to CST America / Chicago"
    },
    {
      "parameters": {
        "calendar": {
          "__rl": true,
          "value": "justinrpandanan@gmail.com",
          "mode": "list",
          "cachedResultName": "justinrpandanan@gmail.com"
        },
        "start": "={{ $json.starttime }}",
        "end": "={{ $json.endtime }}",
        "additionalFields": {
          "allday": "no",
          "attendees": [
            "={{ $json.email }}"
          ],
          "conferenceDataUi": {
            "conferenceDataValues": {
              "conferenceSolution": "hangoutsMeet"
            }
          },
          "description": "={{ $json.notes }}",
          "showMeAs": "opaque",
          "summary": "={{ $json.title }}"
        }
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        1248,
        432
      ],
      "id": "1c28ef8d-14b6-4e76-9d16-35423e722ce7",
      "name": "Create Event",
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "34ee037a-e1d5-4b43-a7ea-258680db7b39",
              "name": "id",
              "value": "={{ $json.id }}",
              "type": "string"
            },
            {
              "id": "d1c4b1da-fcfd-4368-81c6-7e1fb79d9219",
              "name": "description",
              "value": "={{ $json.description }}",
              "type": "string"
            },
            {
              "id": "3aa763c6-9e23-46d0-88f6-ebf84ebe8ca4",
              "name": "starttime",
              "value": "={{ $json.start.dateTime }}",
              "type": "string"
            },
            {
              "id": "8f3a9789-02a2-4b7f-8223-7b7e8de257b6",
              "name": "status",
              "value": "={{ $json.status }}",
              "type": "string"
            },
            {
              "id": "194ccc37-5d55-4032-bc8e-7f38f8f42d70",
              "name": "Timezone",
              "value": "={{ $json.end.timeZone }}",
              "type": "string"
            },
            {
              "id": "ac2944a1-f528-4aa4-9820-2e1571e1b592",
              "name": "attendees",
              "value": "={{ $json.attendees }}",
              "type": "array"
            },
            {
              "id": "babc3321-843c-4ceb-98c6-d659ac318e92",
              "name": "hangoutLink",
              "value": "={{ $json.hangoutLink }}",
              "type": "string"
            },
            {
              "id": "1b11ca4e-72d3-48f3-8f4a-addc0b70245a",
              "name": "Title",
              "value": "={{ $json.summary }}",
              "type": "string"
            },
            {
              "id": "a8dd3f02-a0cf-44e9-956b-4730ac24b9ff",
              "name": "endTime",
              "value": "={{ $json.end.dateTime }}",
              "type": "string"
            }
          ]
        },
        "options": {
          "ignoreConversionErrors": true
        }
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1456,
        336
      ],
      "id": "4b418914-137e-4cc9-91d2-593201921186",
      "name": "Booking Payload"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "9f7c4182-a328-4ecb-9a70-8ef3c931a7f6",
              "name": "results[0].toolCallId",
              "value": "={{ $('input Arguments from booking tools').item.json.toolCallId }}",
              "type": "string"
            },
            {
              "id": "adae36e4-e449-49e6-8a32-26c6c1d916aa",
              "name": "results[0].result",
              "value": "={{ $json.status }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1664,
        336
      ],
      "id": "6b933be2-0565-4b4f-a86e-948f2e97f150",
      "name": "Success Response"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n    \"results\":[\n        {\n            \"toolCallId\":\"{{ $json.results[0].toolCallId }}\",\n            \"result\":\"{{ $json.results[0].result }}\"\n        }\n    ]\n}\n",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1872,
        416
      ],
      "id": "530f627f-f227-46de-89e4-467a7bb5ff7c",
      "name": "Respond to Vapi"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "2964ac62-bd8f-4af1-b1d3-0582f5f8dd65",
              "leftValue": "={{ $json.results[0].result }}",
              "rightValue": "confirmed",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [
        2080,
        416
      ],
      "id": "49be4a07-f8f5-4f1e-8701-3fcaf9be98b2",
      "name": "If the booking is confirmed"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "2955d311-8578-4723-84be-d557844f1c83",
              "name": "startTime",
              "value": "={{ $('Booking Payload').item.json.starttime }}",
              "type": "string"
            },
            {
              "id": "5b3502d3-14a3-47dc-8c9a-7c4139173c76",
              "name": "endTime",
              "value": "={{ $('Booking Payload').item.json.endTime }}",
              "type": "string"
            },
            {
              "id": "a6609b02-513c-40ba-90a5-9c2c46babe57",
              "name": "status",
              "value": "={{ $('Booking Payload').item.json.status }}",
              "type": "string"
            },
            {
              "id": "7befa19e-66ca-4cbb-bcbd-c5a875293265",
              "name": "Timezone",
              "value": "={{ $('Booking Payload').item.json.Timezone }}",
              "type": "string"
            },
            {
              "id": "d88f45f8-068e-4cf5-a08d-4e4d0aaf7792",
              "name": "description",
              "value": "={{ $('Booking Payload').item.json.description }}",
              "type": "string"
            },
            {
              "id": "7402169d-b457-4531-b900-fd4269c282ab",
              "name": "attendees[0].responseStatus",
              "value": "={{ $('Booking Payload').item.json.attendees[0].responseStatus }}",
              "type": "string"
            },
            {
              "id": "e5ecfde7-35bd-4011-b364-1a0c78f22c68",
              "name": "attendees[0].email",
              "value": "={{ $('Booking Payload').item.json.attendees[0].email }}",
              "type": "string"
            },
            {
              "id": "b1306dd7-3d07-4250-a914-01aa3a14b3a3",
              "name": "hangoutLink",
              "value": "={{ $('Booking Payload').item.json.hangoutLink }}",
              "type": "string"
            },
            {
              "id": "0f49ce08-fac8-4dcd-b87e-ef27447bfc34",
              "name": "attendee.name",
              "value": "={{ $('input Arguments from booking tools').item.json.name }}",
              "type": "string"
            },
            {
              "id": "e8204d57-f17e-4ed8-8013-406a4d88691b",
              "name": "call.id",
              "value": "={{ $('BookSlots').item.json.body.message.toolCalls[0].id }}",
              "type": "string"
            },
            {
              "id": "873fb9c6-b19f-42dc-901f-b7395ab63dbb",
              "name": "assistant.name",
              "value": "={{ $json.assistant_name || \"Riley\" }}",
              "type": "string"
            },
            {
              "id": "c26f35b5-ef58-4d0b-ba30-85a3156b3900",
              "name": "event.id",
              "value": "={{ $('Booking Payload').item.json.id }}",
              "type": "string"
            },
            {
              "id": "5e2d3725-e20a-4974-b068-aed17d6c2921",
              "name": "Title",
              "value": "={{ $('Booking Payload').item.json.Title }}",
              "type": "string"
            },
            {
              "id": "4955ad55-49dc-4e80-9c9f-817729d79987",
              "name": "Customer_number",
              "value": "={{ $('input Arguments from booking tools').item.json.customer_number }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2288,
        416
      ],
      "id": "5bf8389c-a328-4b46-9d9f-8e86cb92abff",
      "name": "Information to be Saved in Airtable"
    },
    {
      "parameters": {
        "operation": "create",
        "base": {
          "__rl": true,
          "value": "appGHLTTtkLGarIhY",
          "mode": "list",
          "cachedResultName": "AI Appointment Setter Build",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY"
        },
        "table": {
          "__rl": true,
          "value": "tblF8LF9lmkHMbk7v",
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY/tblF8LF9lmkHMbk7v"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Email": "={{ $json.attendees[0].email }}",
            "Phone Number": "={{ $json.Customer_number }}",
            "Name": "={{ $json.attendee.name }}",
            "Booking Status": "={{ $json.status }}",
            "CallRecordingId": "={{ $json.call.id }}",
            "starttime": "={{ $json.startTime }}",
            "endtime": "={{ $json.endTime }}",
            "meetlink": "={{ $json.hangoutLink }}",
            "Voice Agent": "={{ $json.assistant.name }}",
            "eventId": "={{ $json.event.id }}",
            "meetdescription": "={{ $json.Title }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Email",
              "displayName": "Email",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Phone Number",
              "displayName": "Phone Number",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Name",
              "displayName": "Name",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Booking Status",
              "displayName": "Booking Status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "CallRecordingId",
              "displayName": "CallRecordingId",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "array",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "starttime",
              "displayName": "starttime",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "endtime",
              "displayName": "endtime",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "meetlink",
              "displayName": "meetlink",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "meetdescription",
              "displayName": "meetdescription",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Voice Agent",
              "displayName": "Voice Agent",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "array",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "eventId",
              "displayName": "eventId",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Appointments",
              "displayName": "Appointments",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "typecast": true
        }
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.2,
      "position": [
        2512,
        416
      ],
      "id": "49482776-2396-407f-bdda-fe2e10ba86ba",
      "name": "Log Book Details",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "function replaceValue(value) {\n  if (error.message.include('no_available_users_found_error')) {\n    return \"This time slot is no longer available.\";\n  }\n  return value;\n}\n\n$input.item.json.message = replaceValue($input.item.json.error.description);\n\nreturn $input.item;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1456,
        528
      ],
      "id": "6ca9ff3a-a1fa-46df-9b88-f78a075e3376",
      "name": "Add Friendly Error"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "378e1874-8346-463f-9ac3-927ffa6770d8",
              "name": "=results[0].toolCallId",
              "value": "={{ $('input Arguments from booking tools').item.json.toolCallId }}",
              "type": "string"
            },
            {
              "id": "8d0f6205-5f42-4c64-9eec-f3aaf6557b2f",
              "name": "results[0].result",
              "value": "={{ $json.message || $json.status }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1664,
        528
      ],
      "id": "5cdae37a-843b-4250-bcf7-4751d07ba8e4",
      "name": "Error Response"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "UpdateSlots",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        1056
      ],
      "id": "54fe8984-9445-4578-8884-52862e855490",
      "name": "Update Slots"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "63c6554b-a140-40e7-a7e6-d57b5c953184",
              "name": "calls[0].id",
              "value": "={{ $json.body.message.toolCalls[0].id }}",
              "type": "string"
            },
            {
              "id": "cf41785b-86d7-42a1-acab-e34aed96e3b0",
              "name": "name",
              "value": "={{ $json.safe_arguments.name }}",
              "type": "string"
            },
            {
              "id": "cac194a9-d4ef-47bd-bb23-1a9a95ef3647",
              "name": "email",
              "value": "={{ $json.safe_arguments.email }}",
              "type": "string"
            },
            {
              "id": "2c03eae9-d57f-4494-84b1-f1c4f1adf534",
              "name": "starttime",
              "value": "={{ $json.safe_arguments.starttime }}",
              "type": "string"
            },
            {
              "id": "dc83b07e-c4e5-4c74-9c65-4626133ea647",
              "name": "endtime",
              "value": "={{ $json.safe_arguments.endtime }}",
              "type": "string"
            },
            {
              "id": "524f6ef7-36a1-4840-be12-822defa8f137",
              "name": "call.id",
              "value": "={{ $json.body.message.call.id }}",
              "type": "string"
            },
            {
              "id": "dfe08514-2dce-4b58-9821-c305caa2cb37",
              "name": "Rescheduled_starttime",
              "value": "={{ $json.safe_arguments.Rescheduled_starttime }}",
              "type": "string"
            },
            {
              "id": "81a2c84d-39d1-45c7-8550-fd3521cf6ac1",
              "name": "Rescheduled_endtime",
              "value": "={{ $json.safe_arguments.Rescheduled_endtime }}",
              "type": "string"
            },
            {
              "id": "76bf7e21-98fb-44b6-8037-1f43bd2afeed",
              "name": "customer_number",
              "value": "={{ $json.body.message.call.customer.number }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        432,
        1056
      ],
      "id": "514e67b7-8587-4297-a994-de8b15fd7d6e",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  const toolCalls = item.json.body?.message?.toolCalls || [];\n  \n  if (toolCalls.length === 0) {\n    results.push(item);\n    continue;\n  }\n\n  const toolCall = toolCalls[0];\n  let safe_arguments = {};\n\n  try {\n    // Parse arguments if it's a string, or use directly if already object\n    const args = toolCall.function?.arguments;\n    safe_arguments = typeof args === 'string' ? JSON.parse(args) : args || {};\n  } catch (e) {\n    console.error(\"Parse error:\", e);\n    safe_arguments = {};\n  }\n\n  results.push({\n    json: {\n      ...item.json,\n      toolCallId: toolCall.id,\n      functionName: toolCall.function?.name,\n      safe_arguments: safe_arguments\n    }\n  });\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        1056
      ],
      "id": "025a03ee-1bd1-446a-9b83-337a3e834eb5",
      "name": "Code in JavaScript2"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "660099e6-03bb-4d05-8e9b-a2febf9e362e",
              "leftValue": "={{ $json.name }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "fbe9cda0-f638-4660-8b92-3ef3ce3c50bd",
              "leftValue": "={{ $json.email }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "1f9938f8-0c87-401c-84e4-2e7b828e8886",
              "leftValue": "={{ $json.Rescheduled_starttime }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "53d5cd7b-ebea-4c5b-ae17-8ab8fd75b107",
              "leftValue": "={{ $json.Rescheduled_endtime }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "4844269a-1aef-4a62-8950-222060dd27be",
              "leftValue": "={{ $json.starttime }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": "=",
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        640,
        1056
      ],
      "id": "51aee74a-3e37-495d-b5ca-83e399a5b01a",
      "name": "Check if required info is provided"
    },
    {
      "parameters": {
        "operation": "search",
        "base": {
          "__rl": true,
          "value": "appGHLTTtkLGarIhY",
          "mode": "list",
          "cachedResultName": "AI Appointment Setter Build",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY"
        },
        "table": {
          "__rl": true,
          "value": "tblF8LF9lmkHMbk7v",
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY/tblF8LF9lmkHMbk7v"
        },
        "filterByFormula": "={Phone Number} = (\"{{ $json.customer_number }}\")",
        "options": {
          "fields": [
            "Email",
            "Name",
            "starttime",
            "eventId"
          ]
        }
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.2,
      "position": [
        848,
        976
      ],
      "id": "44b8b833-8c19-48d3-9664-a71e82955394",
      "name": "Find Original Appointment",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "calendar": {
          "__rl": true,
          "value": "justinrpandanan@gmail.com",
          "mode": "list",
          "cachedResultName": "justinrpandanan@gmail.com"
        },
        "eventId": "={{ $json.fields.eventId }}",
        "updateFields": {
          "allday": "no",
          "end": "={{ $('Check if required info is provided').item.json.Rescheduled_endtime }}",
          "start": "={{ $('Check if required info is provided').item.json.Rescheduled_starttime }}"
        }
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        1056,
        976
      ],
      "id": "a21cd9fd-5700-43b2-9861-e583599d6f7b",
      "name": "Update an event",
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "value": "appGHLTTtkLGarIhY",
          "mode": "list",
          "cachedResultName": "AI Appointment Setter Build",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY"
        },
        "table": {
          "__rl": true,
          "value": "tblF8LF9lmkHMbk7v",
          "mode": "list",
          "cachedResultName": "Appointments",
          "cachedResultUrl": "https://airtable.com/appGHLTTtkLGarIhY/tblF8LF9lmkHMbk7v"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "eventId": "={{ $('Find Original Appointment').item.json.fields.eventId }}",
            "Booking Status": "Updated/Rescheduled",
            "starttime": "={{ $json.start.dateTime }}",
            "endtime": "={{ $json.end.dateTime }}"
          },
          "matchingColumns": [
            "eventId"
          ],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "readOnly": true,
              "removed": true
            },
            {
              "id": "Email",
              "displayName": "Email",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "Phone Number",
              "displayName": "Phone Number",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "Name",
              "displayName": "Name",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "Booking Status",
              "displayName": "Booking Status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "CallRecordingId",
              "displayName": "CallRecordingId",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "array",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "starttime",
              "displayName": "starttime",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "endtime",
              "displayName": "endtime",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "meetlink",
              "displayName": "meetlink",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "meetdescription",
              "displayName": "meetdescription",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "Voice Agent",
              "displayName": "Voice Agent",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "array",
              "readOnly": false,
              "removed": true
            },
            {
              "id": "eventId",
              "displayName": "eventId",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Appointments",
              "displayName": "Appointments",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.2,
      "position": [
        1296,
        912
      ],
      "id": "c02730b9-2949-4bdf-9f71-757b047299d6",
      "name": "Update record",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n    \"results\":[\n        {\n            \"toolCallId\":\"{{ $json.results[0].tool.CallId }}\",\n            \"result\":\"{{ $json.results[0].result }}\"\n        }\n    ]\n}\n",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1776,
        992
      ],
      "id": "68a3d730-53d6-4d9d-88e3-e6687e7616b9",
      "name": "Respond to Vapi Updating Slots"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a4b018c0-0ca2-4435-aaa6-9ea7f53835e2",
              "name": "results[0].tool.CallId",
              "value": "={{ $('Code in JavaScript2').item.json.body.message.toolCalls[0].id }}",
              "type": "string"
            },
            {
              "id": "20650143-7ac2-4417-9c4b-c2293e8e7568",
              "name": "results[0].result",
              "value": "={{ $json.error || $json.fields['Booking Status'] }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1520,
        992
      ],
      "id": "a69146c6-77ec-4b15-a52f-ab67f661b692",
      "name": "Response call_ID"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e27f41fa-ba9d-4fcd-8e39-527cbb486fbf",
              "name": "results[0].toolCallId",
              "value": "={{ $json.toolCallId }}",
              "type": "string"
            },
            {
              "id": "8f0134d1-f712-4575-aa8a-edc1b6755c7c",
              "name": "result[0].result",
              "value": "You mus provide an email, name, previous starttime & endtime and rescheduled starttime to call this tool",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        848,
        1152
      ],
      "id": "a4413e04-aa0e-4d32-b3f9-547077b79c0c",
      "name": "Edit Fields1"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1056,
        1152
      ],
      "id": "94955522-af95-4fcc-977f-bc12c450668e",
      "name": "Respond With Error"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "CancelSlots",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        1552
      ],
      "id": "0305ea4e-009e-488e-8b4a-72170b6542ba",
      "name": "Cancel Slots"
    },
    {
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  const toolCalls = item.json.body?.message?.toolCalls || [];\n  \n  if (toolCalls.length === 0) {\n    results.push(item);\n    continue;\n  }\n\n  const toolCall = toolCalls[0];\n  let safe_arguments = {};\n\n  try {\n    const args = toolCall.function?.arguments;\n    safe_arguments = typeof args === 'string' ? JSON.parse(args) : (args || {});\n  } catch (e) {\n    console.error(\"Parse error:\", e);\n  }\n\n  results.push({\n    json: {\n      ...item.json,\n      toolCallId: toolCall.id,\n      functionName: toolCall.function?.name,\n      safe_arguments: safe_arguments\n    }\n  });\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        1552
      ],
      "id": "e16f9418-58aa-4b21-9314-7e850beb759d",
      "name": "Code in JavaScript3"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "777b11bc-9878-4c78-b744-c070889b82dc",
              "name": "calls[0].id",
              "value": "={{ $json.body.message.toolCalls[0].id }}",
              "type": "string"
            },
            {
              "id": "6d4b6b9f-4922-42ed-9d82-69c4e878465e",
              "name": "name",
              "value": "={{ $json.safe_arguments.name }}",
              "type": "string"
            },
            {
              "id": "1a6dae37-68bb-466a-9789-8621bd906f0a",
              "name": "email",
              "value": "={{ $json.safe_arguments.email }}",
              "type": "string"
            },
            {
              "id": "dbfd49bb-58d1-4902-af68-738f9e439050",
              "name": "starttime",
              "value": "={{ $json.safe_arguments.starttime }}",
              "type": "string"
            },
            {
              "id": "23820431-c720-407e-a481-87e93302687d",
              "name": "Cancelnotes",
              "value": "={{ $json.safe_arguments.Cancelnotes }}",
              "type": "string"
            },
            {
              "id": "714429da-d37b-408c-aa50-84b09e593d1b",
              "name": "call.id",
              "value": "={{ $json.body.message.call.id }}",
              "type": "string"
            },
            {
              "id": "85e0ea06-768b-44a9-8663-6395e65214e5",
              "name": "Customer_number",
              "value": "={{ $json.body.message.call.customer.number }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        432,
        1552
      ],
      "id": "0d977406-1877-4abb-9073-5f041ccc2dad",
      "name": "Inpout arguments from cancel slots"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "aad4f188-3d79-4d25-a11d-f442ccbcddc4",
              "leftValue": "={{ $json.name }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "e56beddf-a7c2-4de7-a037-7303d0dc5a82",
              "leftValue": "={{ $json.email }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            },
            {
              "id": "e7d03784-6c03-463f-bdff-5154ee9c88bc",
              "leftValue": "={{ $json.starttime }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
             

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

AI Receptionist. Uses googleCalendar, airtable. Webhook trigger; 59 nodes.

Source: https://github.com/justinrhedgep-star/ai-receptionist-vapi-n8n/blob/main/ai-receptionist-workflow.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

This template is ideal for small businesses, agencies, and solo professionals who want to automate appointment scheduling and caller follow-up through a voice-based AI receptionist. If you’re using to

Item Lists, Google Calendar, Airtable
Data & Sheets

This workflow automates the entire lifecycle of a service-based client, combining four distinct business flows into a single view: Intake Leads: Receives a webhook from your form builder, validates th

Airtable, Notion, Google Calendar +3
Data & Sheets

A warm, reliable onboarding system for small businesses and studios. Captures a form submission via webhook, creates a Client record in Notion, sends a concierge-style welcome email (with scheduler +

Notion, Email Send, Telegram +4
Data & Sheets

AK-Assistance: Anruf beendet. Uses airtable, googleCalendar, httpRequest. Webhook trigger; 5 nodes.

Airtable, Google Calendar, HTTP Request
Data & Sheets

This premium n8n workflow harnesses the power of DataForSEO's API combined with Airtable's relational database capabilities to transform your keyword research process, providing deeper insights for co

HTTP Request, Airtable