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 →
{
"nodes": [
{
"id": "36d0b0d4-b454-4a9b-8168-bcc7942a7cc7",
"name": "Input Arguments",
"type": "n8n-nodes-base.set",
"position": [
520,
740
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ccabe9f4-7911-4488-a75b-7c5779fb2014",
"name": "timeZone",
"type": "string",
"value": "=America/Chicago"
},
{
"id": "b802d976-78f5-4c00-8764-f8c49eaded29",
"name": "endtime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.endtime }}"
},
{
"id": "02d58122-6a0f-4bdb-9914-6f50d2af6df4",
"name": "starttime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.starttime }}"
},
{
"id": "c1249493-a1d7-4a91-9468-9e5c49430d2e",
"name": "body.message.toolCalls[0].id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "2d1e0d9a-4c70-488e-b430-b8137fd54970",
"name": "customer.number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "2d8485ad-9007-4664-9182-7eda25fc96ee",
"name": "Format response",
"type": "n8n-nodes-base.itemLists",
"position": [
2000,
840
],
"parameters": {
"include": "allFieldsExcept",
"options": {},
"aggregate": "aggregateAllItemData",
"operation": "concatenateItems",
"fieldsToExclude": "sort",
"destinationFieldName": "response"
},
"typeVersion": 3
},
{
"id": "b23c75e0-3697-4137-a595-cf26fedaa898",
"name": "Sort",
"type": "n8n-nodes-base.itemLists",
"position": [
1760,
840
],
"parameters": {
"options": {},
"operation": "sort",
"sortFieldsUi": {
"sortField": [
{
"fieldName": "sort"
}
]
}
},
"typeVersion": 3
},
{
"id": "660e3d2f-a424-4e76-8c13-5b62b9f22202",
"name": "Available Start Times & Ranges",
"type": "n8n-nodes-base.code",
"position": [
2240,
840
],
"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 in milliseconds\n\n// Helper to parse CST datetime strings\nconst parseCST = (datetime) => {\n const parsedDate = new Date(datetime.replace(\" CST\", \"-06:00\"));\n return isNaN(parsedDate) ? null : parsedDate;\n};\n\n// Function to generate 30-minute start times\nconst generateStartTimes = (start, end) => {\n const startTimes = [];\n let current = new Date(start);\n\n while (current < end) {\n startTimes.push(\n current.toLocaleTimeString('en-US', {\n timeZone: 'CST',\n hour: '2-digit',\n minute: '2-digit',\n })\n );\n current = new Date(current.getTime() + SLOT_DURATION);\n }\n\n return startTimes;\n};\n\n// Function to find wide open ranges\nconst findWideOpenRanges = (startTimes) => {\n if (startTimes.length < 3) return []; // Not enough slots for a wide open range\n\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 const diff = nextTime - currentTime;\n\n if (diff === SLOT_DURATION) {\n consecutiveCount += 1;\n if (rangeStart === null) rangeStart = startTimes[i];\n } else {\n if (consecutiveCount >= 2) {\n ranges.push(`${rangeStart} to ${startTimes[i]}`);\n }\n rangeStart = null;\n consecutiveCount = 0;\n }\n }\n\n // Handle the final range\n if (consecutiveCount >= 2) {\n ranges.push(`${rangeStart} to ${startTimes[startTimes.length - 1]}`);\n }\n\n return ranges;\n};\n\n// Group meetings by date, ignoring invalid dates\nconst meetingsByDate = inputData.reduce((acc, meeting) => {\n const start = parseCST(meeting.start);\n const end = parseCST(meeting.end);\n\n if (!start || !end) {\n return acc; // Ignore invalid dates\n }\n\n const dateKey = start.toISOString().split('T')[0];\n\n if (!acc[dateKey]) {\n acc[dateKey] = [];\n }\n\n acc[dateKey].push({ start, end });\n return acc;\n}, {});\n\n// Generate availability\nconst availability = Object.keys(meetingsByDate)\n .filter((date) => {\n // Exclude Saturdays (6) and Sundays (0)\n const dayOfWeek = new Date(date).getUTCDay();\n return dayOfWeek !== 0 && dayOfWeek !== 6;\n })\n .map((date) => {\n const workdayStart = parseCST(`${date} ${WORKDAY_START}`);\n const workdayEnd = parseCST(`${date} ${WORKDAY_END}`);\n\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 output as plaintext\nconst availableTimes = availability\n .map(({ date, availableStartTimes, wideOpenRanges }) => {\n const times = availableStartTimes.map((time) => `- ${time}`).join('\\n');\n const ranges = wideOpenRanges.length\n ? `Wide Open Ranges:\\n${wideOpenRanges.map((range) => `- ${range}`).join('\\n')}`\n : \"Wide Open Ranges: None\";\n\n return `### ${date}\\nAvailable Start Times:\\n${times}\\n\\n${ranges}`;\n })\n .join('\\n\\n');\n\n// Set the output\nreturn {\n json: {\n availableTimes,\n },\n};\n"
},
"typeVersion": 2
},
{
"id": "f3110658-2f90-4b19-9874-7d6c4e108895",
"name": "Flatten Slots",
"type": "n8n-nodes-base.code",
"position": [
2460,
840
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const flattenSlots = (data) => {\n // If data is missing or empty, return an empty array of slots\n if (!data) {\n return { slots: [] };\n }\n\n // data is an object whose keys are dates\n // each date key has an array of slot objects\n // we just need to flatten them all into one array\n const flattened = Object.values(data).flat(); // merges all arrays from each date key\n\n // Return a new object with a single 'slots' array\n return { slots: flattened };\n};\n\n// Then assign the flattened slots back to $input.item.json.data\n$input.item.json.data = flattenSlots($input.item.json.data);\nreturn $input.item;\n"
},
"typeVersion": 2
},
{
"id": "5065439e-34e3-4eaf-8226-8ba7393a5cf3",
"name": "Enrich Date",
"type": "n8n-nodes-base.code",
"position": [
2680,
840
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "function formatTimeSlot(dateString) {\n // Format options for date/time with America/Chicago timezone\n const options = {\n timeZone: 'America/Chicago',\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n hour12: true\n };\n\n // Create a formatter with timezone support\n const dateFormatter = new Intl.DateTimeFormat('en-US', options);\n \n // Format the date/time string\n return dateFormatter.format(new Date(dateString));\n}\n\n// Process each slot and add formatted time strings to the result\nconst slots = $input.item.json.data.slots;\nconst formattedSlots = slots.map(slot => formatTimeSlot(slot.start));\n\n// Attach formatted results to the output\n$input.item.json.data.slots = formattedSlots;\n\nreturn $input.item;\n"
},
"typeVersion": 2
},
{
"id": "d8ed3a92-697b-4718-b65f-5276c9a9bfaf",
"name": "Build Response Payload",
"type": "n8n-nodes-base.set",
"position": [
2900,
840
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "5cb05b10-e916-459e-84a2-9c314a859a07",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Input Arguments').item.json.body.message.toolCalls[0].id }}"
},
{
"id": "552246f9-7afd-404e-9fb3-cb38c7447359",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.availableTimes }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a0697944-c5a6-4ca1-9948-8248940841b2",
"name": "Booking Payload",
"type": "n8n-nodes-base.set",
"position": [
1980,
1400
],
"parameters": {
"options": {
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "05bbc797-b781-489c-ab70-e234fe17eb62",
"name": "id",
"type": "number",
"value": "={{ $json.id }}"
},
{
"id": "4bb68abf-18c8-4445-b446-21667abd95aa",
"name": "description",
"type": "string",
"value": "={{ $json.description }}"
},
{
"id": "74a98b77-b9fe-40cc-84c8-fc7303c5cfa6",
"name": "startTime",
"type": "string",
"value": "={{ $json.start.dateTime }}"
},
{
"id": "2934d6a7-9e6b-4038-891c-0b05ba18cb21",
"name": "endTime",
"type": "string",
"value": "={{ $json.end.dateTime }}"
},
{
"id": "10f091c8-5e52-40dc-a294-87625be9af99",
"name": "status",
"type": "string",
"value": "={{ $json.status }}"
},
{
"id": "cdc5e1ab-a29b-447f-8343-ff1c1b168717",
"name": "Timezone",
"type": "string",
"value": "={{ $json.end.timeZone }}"
},
{
"id": "f5b6820c-ab4b-496c-9957-f86753243388",
"name": "attendees",
"type": "array",
"value": "={{ $json.attendees }}"
},
{
"id": "b39a06a5-4fbf-4fdf-9d9a-a07dcb37d157",
"name": "hangoutLink",
"type": "string",
"value": "={{ $json.hangoutLink }}"
},
{
"id": "345f49fc-93bc-48b8-9ced-326139a82119",
"name": "Title",
"type": "string",
"value": "={{ $json.summary }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4f7b157c-f657-48fa-8bb5-a1e074b042eb",
"name": "Success Response",
"type": "n8n-nodes-base.set",
"position": [
2200,
1400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "2c3894da-7bf7-4a35-95c0-d3d9199dd0ad",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Input Arguments from booking tools').item.json.toolCallId }}"
},
{
"id": "685c67c7-a30b-4bcc-b9ba-827c4b570548",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.status }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "b7fe16e3-b625-4cb4-b971-9c26698af89b",
"name": "Add Friendly Error",
"type": "n8n-nodes-base.code",
"position": [
1980,
1760
],
"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;"
},
"typeVersion": 2
},
{
"id": "b5bff0df-2bef-4c43-9fcf-91cadc68b7ca",
"name": "Error Response",
"type": "n8n-nodes-base.set",
"position": [
2200,
1760
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "2c3894da-7bf7-4a35-95c0-d3d9199dd0ad",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Input Arguments from booking tools').item.json.toolCallId }}"
},
{
"id": "93e45166-de94-4fa5-9148-2b8d0e4b960c",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.message || $json.status }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "fe62c0bc-2d73-4f14-8e76-02847ef4e14a",
"name": "Escape Json",
"type": "n8n-nodes-base.code",
"position": [
1260,
1580
],
"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"
},
"typeVersion": 2
},
{
"id": "17927aa4-8f91-4134-b914-1160a724226f",
"name": "Has all information",
"type": "n8n-nodes-base.if",
"position": [
940,
1800
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e0af7f69-0c89-4a02-a49f-dd5a90e31dff",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ ($json.email || \"\").isEmail() }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "6f0bb9e6-9d82-4cc6-a98f-4d00c47ed910",
"name": "Respond with Error",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1480,
1900
],
"parameters": {
"options": {}
},
"typeVersion": 1.1
},
{
"id": "fdedba34-a374-405d-a86e-0b0a1759ede9",
"name": "Build Error Response Payload",
"type": "n8n-nodes-base.set",
"position": [
1260,
1900
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "5cb05b10-e916-459e-84a2-9c314a859a07",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Input Arguments from booking tools').item.json.toolCallId }}"
},
{
"id": "552246f9-7afd-404e-9fb3-cb38c7447359",
"name": "results[0].result",
"type": "string",
"value": "=You must provide an email, name and notes to call this tool"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1e111f9d-bb43-4126-b7a2-3353e7c7c72f",
"name": "Build Error Response Payload2",
"type": "n8n-nodes-base.set",
"position": [
1560,
2840
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "5cb05b10-e916-459e-84a2-9c314a859a07",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Input Arguments from updateslot tool').item.json.toolCallId || $json.Calls[0].id }}"
},
{
"id": "552246f9-7afd-404e-9fb3-cb38c7447359",
"name": "results[0].result",
"type": "string",
"value": "=You must provide an email, name , previous starttime & endtime and resceduled starttime to call this tool"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d6cbad26-d974-4a11-b0fd-2a35bb555378",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
260,
620
],
"parameters": {
"color": 4,
"width": 190,
"height": 80,
"content": "# Get Slots"
},
"typeVersion": 1
},
{
"id": "bcccc8cb-2e9d-4f8b-9964-e4d656e794ed",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
740,
920
],
"parameters": {
"width": 230,
"height": 80,
"content": "## Check Availability\n"
},
"typeVersion": 1
},
{
"id": "30b34e37-ee7a-434c-ab4d-445df994459a",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1320,
520
],
"parameters": {
"width": 310,
"height": 80,
"content": "## If time available Respond\n"
},
"typeVersion": 1
},
{
"id": "725a9b59-ea66-4326-a410-93a723157ced",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
1020
],
"parameters": {
"width": 190,
"height": 80,
"content": "## Get All Events\n"
},
"typeVersion": 1
},
{
"id": "1f2bf4a3-8aeb-4a56-8bff-0bb370e12718",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2140,
1020
],
"parameters": {
"width": 350,
"height": 100,
"content": "## Get Available Slots\n\nFormat the slots and Enrich the date and timings\n"
},
"typeVersion": 1
},
{
"id": "5909d88f-b9c6-4e62-b1e3-bdc1d05ad7aa",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
3280,
1000
],
"parameters": {
"width": 230,
"height": 80,
"content": "## Respond to Vapi"
},
"typeVersion": 1
},
{
"id": "f627c5b0-f3b6-4f95-a3a3-2c1b7e2860c7",
"name": "Sticky Note BookSlot Webhook",
"type": "n8n-nodes-base.stickyNote",
"position": [
380,
1680
],
"parameters": {
"color": 5,
"width": 190,
"height": 80,
"content": "# Book Slot"
},
"typeVersion": 1
},
{
"id": "9c3e8b9f-3fe3-4380-8cbc-413146d752b9",
"name": "Sticky Note BookSlot Check",
"type": "n8n-nodes-base.stickyNote",
"position": [
880,
1700
],
"parameters": {
"width": 230,
"height": 80,
"content": "Checks if required booking info (email, name, etc.) is provided."
},
"typeVersion": 1
},
{
"id": "c723bbd0-5a04-4efb-ba67-59bc722b9d4e",
"name": "Sticky Note BookSlot Error",
"type": "n8n-nodes-base.stickyNote",
"position": [
1440,
2060
],
"parameters": {
"width": 190,
"height": 80,
"content": "If info missing, sends error back."
},
"typeVersion": 1
},
{
"id": "a843e795-8046-4538-93e0-2de2e688c863",
"name": "Sticky Note BookSlot GCal",
"type": "n8n-nodes-base.stickyNote",
"position": [
1660,
1740
],
"parameters": {
"width": 190,
"height": 80,
"content": "Books the appointment in Google Calendar."
},
"typeVersion": 1
},
{
"id": "a7627281-15fc-438a-b031-b00cbc4b9fa4",
"name": "Sticky Note BookSlot Error Handle",
"type": "n8n-nodes-base.stickyNote",
"position": [
1920,
1920
],
"parameters": {
"width": 230,
"height": 80,
"content": "Handles potential booking errors (e.g., slot taken)."
},
"typeVersion": 1
},
{
"id": "71c0c722-b5df-47d7-97e6-3d23533a4a4e",
"name": "Sticky Note BookSlot Response",
"type": "n8n-nodes-base.stickyNote",
"position": [
2420,
1740
],
"parameters": {
"width": 210,
"height": 80,
"content": "Sends confirmation/error back to Vapi."
},
"typeVersion": 1
},
{
"id": "4e598ebb-cfdb-432f-a01a-bb76d1d20f24",
"name": "Sticky Note BookSlot Airtable",
"type": "n8n-nodes-base.stickyNote",
"position": [
3100,
1740
],
"parameters": {
"width": 230,
"height": 80,
"content": "Logs the confirmed booking details to Airtable."
},
"typeVersion": 1
},
{
"id": "cc085c75-d45f-4453-b78b-1b9b480fb02c",
"name": "Sticky Note CancelSlot Webhook",
"type": "n8n-nodes-base.stickyNote",
"position": [
440,
3480
],
"parameters": {
"color": 3,
"width": 250,
"height": 80,
"content": "# Cancel Slots"
},
"typeVersion": 1
},
{
"id": "9504b7e8-7964-4a0e-bfa4-32540c1fb895",
"name": "Sticky Note CancelSlot Check",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
3780
],
"parameters": {
"width": 230,
"height": 80,
"content": "Checks if required info (email, name, start time) is provided."
},
"typeVersion": 1
},
{
"id": "3bb9d976-d922-4016-839c-22e8b1adcf35",
"name": "Sticky Note CancelSlot Error",
"type": "n8n-nodes-base.stickyNote",
"position": [
1520,
3940
],
"parameters": {
"width": 150,
"height": 80,
"content": "If info missing, sends error back."
},
"typeVersion": 1
},
{
"id": "87442b3a-b8eb-43e6-b15d-0240a58bff79",
"name": "Sticky Note CancelSlot Search",
"type": "n8n-nodes-base.stickyNote",
"position": [
1300,
3440
],
"parameters": {
"width": 190,
"height": 100,
"content": "Finds the appointment record in Airtable by phone number to get event ID."
},
"typeVersion": 1
},
{
"id": "4e0cec59-1acc-4604-80ce-09479c7a6652",
"name": "Sticky Note CancelSlot GCal Delete",
"type": "n8n-nodes-base.stickyNote",
"position": [
1720,
3720
],
"parameters": {
"width": 190,
"height": 80,
"content": "Deletes the event from Google Calendar using event ID."
},
"typeVersion": 1
},
{
"id": "68e00556-93d2-45a8-9fee-deb1477ffff2",
"name": "Sticky Note CancelSlot Airtable Update",
"type": "n8n-nodes-base.stickyNote",
"position": [
2060,
3400
],
"parameters": {
"width": 190,
"height": 80,
"content": "Updates Airtable record status to 'Canceled'."
},
"typeVersion": 1
},
{
"id": "5853b0f6-1e73-435e-aff8-5d9d8de53693",
"name": "Sticky Note CancelSlot Response",
"type": "n8n-nodes-base.stickyNote",
"position": [
2380,
3740
],
"parameters": {
"width": 190,
"height": 80,
"content": "Sends cancellation confirmation/error back to Vapi."
},
"typeVersion": 1
},
{
"id": "660cdb51-84ac-434e-b7d8-f7b17ef7ef5b",
"name": "Sticky Note UpdateSlot Webhook",
"type": "n8n-nodes-base.stickyNote",
"position": [
460,
2600
],
"parameters": {
"color": 6,
"width": 250,
"height": 80,
"content": "# Update Slots"
},
"typeVersion": 1
},
{
"id": "030e4bce-4b8b-42b7-8cf4-86b3a88f375b",
"name": "Sticky Note UpdateSlot Check",
"type": "n8n-nodes-base.stickyNote",
"position": [
1000,
2900
],
"parameters": {
"width": 230,
"height": 80,
"content": "Checks if required info (email, name, old/new times) is provided."
},
"typeVersion": 1
},
{
"id": "02e9f8e3-7561-4ace-95a2-2b1807940f1a",
"name": "Sticky Note UpdateSlot Error",
"type": "n8n-nodes-base.stickyNote",
"position": [
1500,
3020
],
"parameters": {
"width": 190,
"height": 80,
"content": "If info missing, sends error back."
},
"typeVersion": 1
},
{
"id": "4820cb6c-de15-4e9a-bca7-e3f172af6b80",
"name": "Sticky Note UpdateSlot Search",
"type": "n8n-nodes-base.stickyNote",
"position": [
1580,
2460
],
"parameters": {
"width": 190,
"height": 80,
"content": "Finds original appointment in Airtable by old phone number"
},
"typeVersion": 1
},
{
"id": "5dd3075e-8e0b-4f76-8d21-39aa66d449da",
"name": "Sticky Note UpdateSlot GCal Update",
"type": "n8n-nodes-base.stickyNote",
"position": [
1940,
2720
],
"parameters": {
"width": 190,
"height": 80,
"content": "Updates the event time in Google Calendar."
},
"typeVersion": 1
},
{
"id": "2e08b4f6-f279-4a9e-9bd3-d6a6283b45f4",
"name": "Sticky Note UpdateSlot Airtable Update",
"type": "n8n-nodes-base.stickyNote",
"position": [
2240,
2320
],
"parameters": {
"width": 170,
"height": 100,
"content": "Updates Airtable record with new times & 'Updated' status."
},
"typeVersion": 1
},
{
"id": "d1369c19-401e-4ce2-a21e-0d5ae01af119",
"name": "Sticky Note UpdateSlot Response",
"type": "n8n-nodes-base.stickyNote",
"position": [
2720,
2460
],
"parameters": {
"width": 230,
"height": 80,
"content": "Sends rescheduling confirmation/error back to Vapi."
},
"typeVersion": 1
},
{
"id": "ca064fbe-b175-443f-b8e8-70a2a7551ba9",
"name": "Sticky Note CallResults Webhook",
"type": "n8n-nodes-base.stickyNote",
"position": [
440,
4160
],
"parameters": {
"color": 2,
"width": 390,
"height": 120,
"content": "# Call Result logs\nReceives call summary and recording details post-call."
},
"typeVersion": 1
},
{
"id": "4941246b-82d1-4f16-b7b8-e1fdb6e7c833",
"name": "Sticky Note CallResults Airtable",
"type": "n8n-nodes-base.stickyNote",
"position": [
860,
4460
],
"parameters": {
"width": 230,
"height": 80,
"content": "Logs call transcript, recording URL, summary, cost, customer number to Airtable."
},
"typeVersion": 1
},
{
"id": "e5622e9e-9b0a-43b2-ab80-e3e33a4b0409",
"name": "Getslot_tool",
"type": "n8n-nodes-base.webhook",
"position": [
260,
740
],
"parameters": {
"path": "getslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "e15781cf-5405-4f60-aa6d-ba19d1b7dabc",
"name": "Check Availability",
"type": "n8n-nodes-base.googleCalendar",
"position": [
800,
740
],
"parameters": {
"options": {},
"timeMax": "={{ $json.endtime.toDateTime() || $now.plus(1, 'hour').toISO() }}",
"timeMin": "={{ $json.starttime.toDateTime() }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "pratik@customaistudio.io",
"cachedResultName": "pratik@customaistudio.io"
},
"resource": "calendar"
},
"credentials": {},
"typeVersion": 1.3
},
{
"id": "1e064283-2964-4eba-a893-e4270157c603",
"name": "Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1540,
640
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $('Getslot_tool').first().json.body.message.toolCalls[0].id }}\",\n \"result\":\"available:{{ $json.available }}\"\n }\n ]\n}"
},
"typeVersion": 1.1
},
{
"id": "498401cb-00e5-4fdd-b6a9-dd3e91376993",
"name": "Check if time is available or not",
"type": "n8n-nodes-base.if",
"position": [
1020,
740
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "4a8741a2-a903-4fb7-b0a3-5c74c7eea6ca",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.available }}",
"rightValue": "="
}
]
}
},
"typeVersion": 2.2
},
{
"id": "96e43c15-a332-4acf-af04-80dd989d5660",
"name": "Time available (true) & Call_id",
"type": "n8n-nodes-base.set",
"position": [
1320,
640
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f582d965-af15-4ecf-8a8c-d8bf6c0d15c1",
"name": "body.message.toolCalls[0].id",
"type": "string",
"value": "={{ $('Input Arguments').item.json.body.message.toolCalls[0].id }}"
},
{
"id": "834ee925-5c8d-4e46-aeee-f399dc1ff40c",
"name": "available",
"type": "boolean",
"value": "={{ $json.available }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fb7ad8c6-9f78-4518-b955-60f3f7088cb9",
"name": "Get All Calendar Events",
"type": "n8n-nodes-base.googleCalendar",
"position": [
1320,
840
],
"parameters": {
"options": {
"orderBy": "startTime",
"timeMax": "={{ $now.plus(1, 'week').toISO() }}",
"timeMin": "={{ $now.toISO() }}",
"singleEvents": true
},
"calendar": {
"__rl": true,
"mode": "list",
"value": "pratik@customaistudio.io",
"cachedResultName": "pratik@customaistudio.io"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "390599ee-ddeb-4628-af0b-36fbdd357cee",
"name": "Extract start, end and name",
"type": "n8n-nodes-base.set",
"position": [
1540,
840
],
"parameters": {
"options": {
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "1045b97f-c76f-450e-8f57-008602000848",
"name": "start",
"type": "string",
"value": "={{ DateTime.fromISO($json.start.dateTime).toLocaleString(DateTime.DATE_HUGE) }}, {{ DateTime.fromISO($json.start.dateTime).toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET) }}"
},
{
"id": "457e3a2b-d33e-4a65-b2da-d19ad9d754ac",
"name": "end",
"type": "string",
"value": "={{ DateTime.fromISO($json.end.dateTime).toLocaleString(DateTime.DATE_HUGE) }}, {{ DateTime.fromISO($json.end.dateTime).toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET) }}"
},
{
"id": "b6802452-557e-4568-af14-4574e8ecc013",
"name": "name",
"type": "string",
"value": "={{ $json.summary }}"
},
{
"id": "799b656f-68b6-467c-88a1-217ff7c7801b",
"name": "sort",
"type": "string",
"value": "={{ $json.start.dateTime }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2c9a73da-37b7-4abd-af5e-695036cd2c2b",
"name": "Convert into Json format for Vapi",
"type": "n8n-nodes-base.code",
"position": [
3120,
840
],
"parameters": {
"jsCode": "// Get the input data for the first item\nconst inputData = $input.first().json;\nconsole.log(\"Input Data:\", inputData); // Log input for debugging\n\n// Access the message string from the correct path within the input structure.\n// The input comes from the \"Build Response Payload\" node, which structures data under 'results'.\n// Use optional chaining (?.) for safety in case the structure is not as expected.\nlet message = inputData.results?.[0]?.result;\n\n// Check if the message was found and is a string\nif (typeof message !== 'string') {\n console.error(\"Could not find the message string at inputData.results[0].result or it's not a string. Input:\", inputData);\n // Return an object with an empty message or an error indicator\n return { message: \"\" }; // Or potentially throw an error: throw new Error(\"Input message not found or not a string\");\n}\n\n// Start cleaning the message string\n\n// 1. Replace the literal string \"\\\\n\" (backslash followed by n) with a space.\n// This handles the newline representation seen in the input screenshot.\nlet cleanedMessage = message.replace(/\\\\n/g, ' ');\n\n// 2. Remove spaces immediately surrounding colons (e.g., \"Times : \" becomes \"Times:\").\ncleanedMessage = cleanedMessage.replace(/\\s*:\\s*/g, ':');\n\n// 3. Replace sequences of multiple whitespace characters (including spaces from replaced \\n)\n// with a single space. Then, trim any leading or trailing whitespace from the result.\ncleanedMessage = cleanedMessage.replace(/\\s+/g, ' ').trim();\n\n// Create the final output JSON object containing the cleaned message.\nconst output = {\n message: cleanedMessage\n};\n\n// Return the output object. This will be the output of the Code node.\nreturn output;"
},
"typeVersion": 2
},
{
"id": "e00cf72a-af6a-441b-9b76-81bd8096d3df",
"name": "Response to Vapi",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3360,
840
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $('Getslot_tool').first().json.body.message.toolCalls[0].id }}\",\n \"result\":\"The original time is not available, here are available slots:{{ $json.message }}\"\n }\n ]\n}"
},
"typeVersion": 1.1
},
{
"id": "facf3bf9-e05e-4953-a221-bf7f566a3b0f",
"name": "bookslots_tool",
"type": "n8n-nodes-base.webhook",
"position": [
400,
1800
],
"parameters": {
"path": "bookslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "9266904b-300f-4c83-a518-4cd69b13de41",
"name": "Input Arguments from booking tools",
"type": "n8n-nodes-base.set",
"position": [
720,
1800
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "eac930a3-ba65-4b0d-b236-aa167d7edb3f",
"name": "toolCallId",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "492186b8-e3a3-4ab9-87f4-45d8cbc38c13",
"name": "timeZone",
"type": "string",
"value": "=America/Chicago"
},
{
"id": "12aeec42-9414-4d43-8837-1ff747f49305",
"name": "name",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.name || \"John Smith\" }}"
},
{
"id": "36673f27-c026-4ad9-81da-ad11e71bbfb6",
"name": "email",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.email }}"
},
{
"id": "469ddc00-a399-47a5-8c55-97cd3adf4143",
"name": "language",
"type": "string",
"value": "en"
},
{
"id": "b191cd98-f3f7-48b1-a2e0-2c9e248a4983",
"name": "notes",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.notes || \"\"}}"
},
{
"id": "783cb161-65e4-4829-ac90-5c6c2c55585f",
"name": "starttime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.starttime }}"
},
{
"id": "bfcdade9-14c8-4867-8a22-3865a2bcc116",
"name": "endtime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.endtime }}"
},
{
"id": "26ca39ef-48f5-41ed-990e-40c2a26d6132",
"name": "Tittle",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.Title }}"
},
{
"id": "43575f7a-3873-4d74-90c5-4467c7779514",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "b4bc5cee-d631-4aa8-a3ff-59a0b647d36a",
"name": "Convert time to CST America / Chicago",
"type": "n8n-nodes-base.code",
"position": [
1480,
1580
],
"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;"
},
"typeVersion": 2
},
{
"id": "2c8b2884-14d3-4bd4-92d8-6e402ca3a8de",
"name": "Create Event",
"type": "n8n-nodes-base.googleCalendar",
"onError": "continueErrorOutput",
"position": [
1700,
1580
],
"parameters": {
"end": "={{ $json.endtime }}",
"start": "={{ $json.starttime }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "pratik@customaistudio.io",
"cachedResultName": "pratik@customaistudio.io"
},
"additionalFields": {
"allday": "no",
"summary": "={{ $json.Tittle }}",
"showMeAs": "opaque",
"attendees": [
"={{ $json.email }}"
],
"description": "={{ $json.notes }}",
"conferenceDataUi": {
"conferenceDataValues": {
"conferenceSolution": "hangoutsMeet"
}
}
}
},
"credentials": {},
"typeVersion": 1.3
},
{
"id": "b8890ab2-9850-4608-996d-45c8a6d3a52e",
"name": "Respond to Vapi",
"type": "n8n-nodes-base.respondToWebhook",
"onError": "continueRegularOutput",
"position": [
2480,
1580
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $json.results[0].toolCallId }}\",\n \"result\":\"available:{{ $json.results[0].result }}\"\n }\n ]\n}"
},
"typeVersion": 1.1,
"alwaysOutputData": true
},
{
"id": "77f75f42-46bb-47f5-8a43-55543ae46f10",
"name": "If the booking is confirmed then true",
"type": "n8n-nodes-base.if",
"position": [
2700,
1580
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "932dd430-309b-4d3b-8bf6-768f84fd2dd2",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.results[0].result }}",
"rightValue": "=confirmed"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "230ddb29-67f0-4486-a6f3-f4dd3dbbee42",
"name": "Information to be Saved in Airtable",
"type": "n8n-nodes-base.set",
"position": [
2940,
1560
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "b103265d-86da-4256-994d-85a78f33f933",
"name": "startTime",
"type": "string",
"value": "={{ $('Booking Payload').item.json.startTime }}"
},
{
"id": "a8e6e9c5-6ebb-48d8-951f-b007bed2421d",
"name": "endTime",
"type": "string",
"value": "={{ $('Booking Payload').item.json.endTime }}"
},
{
"id": "d4bcb1d1-043a-4205-8488-0a67b4e7b582",
"name": "status",
"type": "string",
"value": "={{ $('Booking Payload').item.json.status }}"
},
{
"id": "92ac8c99-ad94-4b3c-9c5e-ba032dac2255",
"name": "description",
"type": "string",
"value": "={{ $('Booking Payload').item.json.description }}"
},
{
"id": "98c5653d-1e0e-4a6a-8630-17802d437593",
"name": "attendees[0].email",
"type": "string",
"value": "={{ $('Booking Payload').item.json.attendees[0].email }}"
},
{
"id": "f94bdfc1-dc74-4675-ad29-19244fb21ebe",
"name": "attendees[0].responseStatus",
"type": "string",
"value": "={{ $('Booking Payload').item.json.attendees[0].responseStatus }}"
},
{
"id": "12bd5ed5-4934-4c19-a9b9-54fe989eaa4f",
"name": "hangoutLink",
"type": "string",
"value": "={{ $('Booking Payload').item.json.hangoutLink }}"
},
{
"id": "5b1f9356-7d62-4999-ae4e-86f3f20d72bf",
"name": "attendee.name",
"type": "string",
"value": "={{ $('bookslots_tool').item.json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "6e93805e-8754-4f92-870f-7b46525f3eb3",
"name": "call.id",
"type": "string",
"value": "={{ $('bookslots_tool').item.json.body.message.call.id }}"
},
{
"id": "f174e2be-3230-4fc9-970b-971aff6e9b8e",
"name": "assistant.name",
"type": "string",
"value": "={{ $('bookslots_tool').item.json.body.message.assistant.name }}"
},
{
"id": "a4bc9d70-7d51-487f-b622-433e767ef71f",
"name": "event.id",
"type": "string",
"value": "={{ $('Create Event').item.json.id }}"
},
{
"id": "9259b1d3-3658-4ab5-b434-364e6a84d145",
"name": "Title",
"type": "string",
"value": "={{ $('Booking Payload').item.json.Title }}"
},
{
"id": "2102a7be-5d74-458f-bafd-21651e24adb1",
"name": "customer_number",
"type": "string",
"value": "={{ $('Input Arguments from booking tools').item.json.customer_number}}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f6c7774d-a8c7-466a-ba77-401194fe6fb4",
"name": "Logs the confirmed booking details",
"type": "n8n-nodes-base.airtable",
"position": [
3160,
1560
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appnj853UnMRnJ8D3",
"cachedResultUrl": "https://airtable.com/appnj853UnMRnJ8D3",
"cachedResultName": "Voice Receptionist Agent"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/appnj853UnMRnJ8D3/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"columns": {
"value": {
"Name": "={{ $json.attendee.name }}",
"Email": "={{ $json.attendees[0].email }}",
"endtime": "={{ $json.endTime }}",
"eventId": "={{ $json.event.id }}",
"meetlink": "={{ $json.hangoutLink }}",
"starttime": "={{ $json.startTime }}",
"Voice Agent": "={{ [$json.assistant.name] }}",
"Phone Number": "={{ $json.customer_number }}",
"Booking Status": "={{ $json.status }}",
"CallRecordingId": "={{ [$json.call.id] }}",
"meetdescription": "={{ $json.Title }} {{ $json.description }}"
},
"schema": [
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone Number",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Phone Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Booking Status",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Booking Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "CallRecordingId",
"type": "array",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "CallRecordingId",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "starttime",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "starttime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "endtime",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "endtime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meetlink",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "meetlink",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meetdescription",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "meetdescription",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Agent",
"type": "array",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Voice Agent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "eventId",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "eventId",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Appointments",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Appointments",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"typecast": true
},
"operation": "create"
},
"credentials": {},
"typeVersion": 2.1
},
{
"id": "154bee14-9281-4b92-8204-57c5436785ba",
"name": "Updateslots_tool",
"type": "n8n-nodes-base.webhook",
"position": [
460,
2720
],
"parameters": {
"path": "updateslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "891fb4ec-3a82-4433-bebf-3f0616027e3d",
"name": "Input Arguments from updateslot tool",
"type": "n8n-nodes-base.set",
"position": [
840,
2720
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "6f6388ab-a233-4643-9b28-917ad6bdfe22",
"name": "Calls[0].id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "40888d2c-b99d-401d-a6b9-944ba41543c6",
"name": "name",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "17be6cf6-8c48-4a4e-a0e8-b5b714f94242",
"name": "email",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.email }}"
},
{
"id": "d06fd547-39c1-457b-8422-393f140aead6",
"name": "starttime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.starttime }}"
},
{
"id": "c224df67-ec82-40f3-9af2-3472731a57fa",
"name": "endtime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.endtime }}"
},
{
"id": "b2fb0887-5545-409c-bba8-fae76a71f660",
"name": "call.id",
"type": "string",
"value": "={{ $json.body.message.call.id }}"
},
{
"id": "19efa4c6-25e0-4fe8-a00e-0b37f16b6de0",
"name": "Rescheduled_starttime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.Rescheduled_starttime }}"
},
{
"id": "ad47dfdb-66fa-478d-899f-1d9d202aac6f",
"name": "Rescheduled_endttime",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.Rescheduled_endttime }}"
},
{
"id": "6d1bf6c0-a4b4-41d4-826e-e7c73f920905",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "617a7742-299a-4c91-be82-cba598d1bb82",
"name": "Checks if required info is provided.",
"type": "n8n-nodes-base.if",
"position": [
1060,
2720
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "87304425-5f17-4637-8aa3-cd84b2f8d856",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.name }}",
"rightValue": ""
},
{
"id": "fdc6ffb0-f234-4869-8f5e-482c394ab860",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
},
{
"id": "7950d7bc-7416-48b6-8ec5-a635a9161013",
"operator": {
"type": "dateTime",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.Rescheduled_starttime }}",
"rightValue": "={{ $json.Rescheduledtime }}"
},
{
"id": "aa54ee15-1273-48b0-863f-939597af04e6",
"operator": {
"type": "dateTime",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.Rescheduled_endttime }}",
"rightValue": ""
},
{
"id": "8ceefa9d-360c-48b6-8faf-e156459f2c07",
"operator": {
"type": "dateTime",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.starttime }}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "dded1cfa-ce89-481f-967b-6843854a32bd",
"name": "Finds original appointment",
"type": "n8n-nodes-base.airtable",
"maxTries": 2,
"position": [
1600,
2560
About this workflow
Webhook Code. Uses itemLists, respondToWebhook, stickyNote, googleCalendar. Webhook trigger; 92 nodes.
Source: https://github.com/Zie619/n8n-workflows — original creator credit. Request a take-down →