Voice AI Receptionist & Scheduler
I built a Vapi.ai receptionist orchestrated by n8n. It autonomously qualifies callers, logs detailed notes to Airtable, and performs real-time availability checks to book appointments directly on Google Calendar.
Impact
• Achieved 24/7 lead capture with zero missed calls
• Automated 100% of initial appointment bookings via voice
• Seamless <1s sync between voice data, Airtable, and Google Calendar
Shared 12/5/2025
2 views
Visual Workflow
JSON Code
{
"id": "Tr9Tz2vMinJ0zv9e",
"meta": {
"instanceId": "385836472bd090bc0a4be4920feadaa2165e97fb24c24504ed3bad6ead56a071",
"templateCredsSetupCompleted": true
},
"name": "AI Receptionist",
"tags": [],
"nodes": [
{
"id": "29d34cd6-2e42-46c6-9986-04a738649cb6",
"name": "Aggregate",
"type": "n8n-nodes-base.aggregate",
"position": [
704,
-272
],
"parameters": {
"include": "allFieldsExcept",
"options": {},
"aggregate": "aggregateAllItemData",
"fieldsToExclude": "sort",
"destinationFieldName": "response"
},
"typeVersion": 1
},
{
"id": "110f7ef7-4608-4755-a0a6-a1427476fb6a",
"name": "Incoming Availability Request",
"type": "n8n-nodes-base.webhook",
"position": [
-640,
-368
],
"webhookId": "55cdf9b8-dbef-45fa-a661-7cddc5dda90e",
"parameters": {
"path": "getslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "80d4dce1-63a9-45f2-b1c4-61594552af6d",
"name": "Normalize Request Data",
"type": "n8n-nodes-base.set",
"position": [
-416,
-368
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fbffe00f-51c9-431f-895d-7f110481be67",
"name": "timezone",
"type": "string",
"value": "America/New_York"
},
{
"id": "055df25a-76c5-4d9d-b300-83c406c6baff",
"name": "start_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.startTime }}"
},
{
"id": "2e5b997d-e1ca-4078-a2fb-c5635cc267ce",
"name": "end_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.endTime }}"
},
{
"id": "61057abc-0a6d-4a4e-9706-e024e134471b",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number || \"0\" }}"
},
{
"id": "fd9562d4-51cc-4aed-be62-d346e0e82fc6",
"name": "call_id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "617c2be2-ba29-482c-b56a-60e533eafcd2",
"name": "Check Slot in Calendar",
"type": "n8n-nodes-base.googleCalendar",
"position": [
-192,
-368
],
"parameters": {
"options": {},
"timeMax": "={{ $json.end_time.toDateTime() || $json.start_time.plus(1, 'hour').toISO() }}",
"timeMin": "={{ $json.start_time.toDateTime() }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
},
"resource": "calendar"
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "2bc2cf99-33ea-4b86-a549-e039bc347d9b",
"name": "Is Requested Slot Free?",
"type": "n8n-nodes-base.if",
"position": [
32,
-368
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9e0bc30f-db28-4d1e-8178-643b7f3b3a9e",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.available }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "202eef39-05db-4890-8bb7-f4cf34490456",
"name": "Return Successful Availability Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
256,
-464
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $('Incoming Availability Request').item.json.body.message.toolCalls[0].id }}\",\n \"result\":\"available:{{ $('Check Slot in Calendar').item.json.available }}\"\n }\n ]\n}"
},
"typeVersion": 1.4
},
{
"id": "d7a1f3e4-3765-4c66-b4e8-a449c763700e",
"name": "Fetch Calendar Events",
"type": "n8n-nodes-base.googleCalendar",
"position": [
256,
-272
],
"parameters": {
"options": {
"orderBy": "startTime"
},
"timeMax": "={{ $now.plus( 1, 'week' ).toISO() }}",
"timeMin": "={{ $now.toISO() }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
},
"operation": "getAll"
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3,
"alwaysOutputData": true
},
{
"id": "eb940238-ea75-4d26-8026-bd870d333179",
"name": "Format Events for Output",
"type": "n8n-nodes-base.set",
"position": [
480,
-272
],
"parameters": {
"options": {
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "c4f7102f-0cfa-4e95-9362-39ea85ba2c96",
"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": "0fca085d-7b51-482e-b2ee-5afdd503d994",
"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": "b3b4ba39-3ef4-4c33-97e2-2bb2a6d3acac",
"name": "name",
"type": "string",
"value": "={{ $json.summary }}"
},
{
"id": "0ce2ed86-a4d4-4a5b-bae6-d85188b40dc1",
"name": "sort",
"type": "string",
"value": "={{ $json.start.dateTime }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4721f121-4c84-4b3d-9559-293f81d17570",
"name": "Assemble Availability Payload",
"type": "n8n-nodes-base.code",
"position": [
928,
-272
],
"parameters": {
"jsCode": "// Input data (your meetings)\nconst inputData = $json.response;\n\n// CONFIG\nconst LOCAL_TZ_OFFSET = 8; // GMT+8 (your timezone)\nconst USER_TZ = \"America/New_York\"; // Prospects timezone (ET)\n\n// Workday in LOCAL TIME\nconst WORKDAY_START = 7 * 60; // 07:00 GMT+8\nconst WORKDAY_END = 22 * 60; // 22:00 GMT+8\nconst SLOT = 30;\n\n// ------------------------------------------------------------\n// MANUAL PARSER FOR YOUR TIMESTAMP FORMAT (GMT+8)\n// ------------------------------------------------------------\nconst parseLocalDate = (raw) => {\n const parts = raw.split(\", \");\n const [monthStr, dayStr] = parts[1].split(\" \");\n const year = Number(parts[2]);\n\n const month = new Date(`${monthStr} 1, 2000`).getMonth();\n const timePart = parts[3].split(\" \")[0];\n const [hh, mm] = timePart.split(\":\").map(Number);\n\n return new Date(Date.UTC(year, month, Number(dayStr), hh - LOCAL_TZ_OFFSET, mm));\n};\n\n// ------------------------------------------------------------\n// Convert local minutes → 12-hour ET time\n// ------------------------------------------------------------\nconst localMinutesToET12h = (dateKey, mins) => {\n const [Y, M, D] = dateKey.split(\"-\").map(Number);\n\n const localMidnightUTC = Date.UTC(Y, M - 1, D, -LOCAL_TZ_OFFSET, 0);\n const dt = new Date(localMidnightUTC + mins * 60000);\n\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: USER_TZ,\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n\n return fmt.format(dt); // No “ET” here\n};\n\n// ------------------------------------------------------------\n// Get LOCAL DATE KEY (YYYY-MM-DD)\n// ------------------------------------------------------------\nconst getLocalDateKey = (raw) => {\n const d = parseLocalDate(raw);\n\n const fmt = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"Asia/Manila\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n\n return fmt.format(d);\n};\n\n// ------------------------------------------------------------\n// Parse LOCAL minutes\n// ------------------------------------------------------------\nconst parseLocalMinutes = (raw) => {\n const d = parseLocalDate(raw);\n\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: \"Asia/Manila\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n });\n\n const parts = fmt.formatToParts(d);\n const hh = Number(parts.find(p => p.type === \"hour\").value);\n const mm = Number(parts.find(p => p.type === \"minute\").value);\n\n return hh * 60 + mm;\n};\n\n// ------------------------------------------------------------\n// Group meetings by local date\n// ------------------------------------------------------------\nconst meetingsByDate = {};\n\nfor (const m of inputData) {\n const dateKey = getLocalDateKey(m.start);\n\n if (!meetingsByDate[dateKey]) meetingsByDate[dateKey] = [];\n meetingsByDate[dateKey].push({\n start: parseLocalMinutes(m.start),\n end: parseLocalMinutes(m.end),\n });\n}\n\n// ------------------------------------------------------------\n// Build availability\n// ------------------------------------------------------------\nconst availability = Object.keys(meetingsByDate).map((dateKey) => {\n const meetings = meetingsByDate[dateKey].sort((a, b) => a.start - b.start);\n\n let free = [];\n let cursor = WORKDAY_START;\n\n for (const m of meetings) {\n for (let t = cursor; t + SLOT <= m.start; t += SLOT) {\n free.push(t);\n }\n cursor = Math.max(cursor, m.end);\n }\n\n for (let t = cursor; t + SLOT <= WORKDAY_END; t += SLOT) {\n free.push(t);\n }\n\n const formatted = free.map(t => localMinutesToET12h(dateKey, t));\n\n const ranges = [];\n let startRange = null;\n let count = 0;\n\n for (let i = 0; i < free.length - 1; i++) {\n if (free[i+1] - free[i] === SLOT) {\n if (!startRange) startRange = free[i];\n count++;\n } else {\n if (count >= 2) {\n ranges.push(\n `${localMinutesToET12h(dateKey, startRange)} to ${localMinutesToET12h(dateKey, free[i])}`\n );\n }\n startRange = null;\n count = 0;\n }\n }\n\n if (count >= 2) {\n ranges.push(\n `${localMinutesToET12h(dateKey, startRange)} to ${localMinutesToET12h(dateKey, free[free.length - 1])}`\n );\n }\n\n const [Y, M, D] = dateKey.split(\"-\").map(Number);\n const midnightLocalUTC = new Date(Date.UTC(Y, M - 1, D, -LOCAL_TZ_OFFSET));\n const friendly = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: USER_TZ,\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\"\n }).format(midnightLocalUTC);\n\n return {\n date: friendly,\n availableStartTimes: formatted,\n wideOpenRanges: ranges\n };\n});\n\n// ------------------------------------------------------------\n// Format output\n// ------------------------------------------------------------\nconst availableTimes = availability\n .map(({ date, availableStartTimes, wideOpenRanges }) => {\n const times = availableStartTimes.map(t => `- ${t}`).join(\"\\n\");\n const ranges = wideOpenRanges.length\n ? `Wide Open Ranges:\\n${wideOpenRanges.map(r => `- ${r}`).join(\"\\n\")}`\n : \"Wide Open Ranges: None\";\n\n return `### ${date}\\n(All times in ET)\\nAvailable Start Times:\\n${times}\\n\\n${ranges}`;\n })\n .join(\"\\n\\n\");\n\nreturn { json: { availableTimes } };\n"
},
"typeVersion": 2
},
{
"id": "263d9d89-b3fb-46cd-90bd-33cc6836713f",
"name": "Return Available Slots",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1152,
-272
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $('Incoming Availability Request').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}"
},
"typeVersion": 1.4
},
{
"id": "e657b1bb-1e41-43e6-8a6c-31a2466d137e",
"name": "Incoming Booking Request",
"type": "n8n-nodes-base.webhook",
"position": [
-640,
144
],
"webhookId": "0ab590e5-b44f-4ef7-8569-e27de48a3fe3",
"parameters": {
"path": "bookslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "f2f50633-9956-4d01-88ca-48d572c151be",
"name": "Normalize Booking Data",
"type": "n8n-nodes-base.set",
"position": [
-416,
144
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3da28297-7a6c-45f8-b6cf-271068752e06",
"name": "call_id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "bcd06f28-7b6d-4c2c-af6b-d21304b208a6",
"name": "timezone",
"type": "string",
"value": "America/New_York"
},
{
"id": "932ef137-6196-47ce-b44d-e05c86cc1e2b",
"name": "name",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "ab2126c9-f7ab-43fc-8007-fc903c3890cb",
"name": "email",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.email }}"
},
{
"id": "4f5fb902-dc27-44e5-90d6-9da64d0aea65",
"name": "language",
"type": "string",
"value": "en"
},
{
"id": "5e567142-db18-48d7-9d0b-7f25abf987a4",
"name": "notes",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.notes }}"
},
{
"id": "1ea6c643-3fee-4a33-a052-347213d7d61a",
"name": "start_time",
"type": "string",
"value": "={{ $json.body.message.toolCallList[0].function.arguments.startTime }}"
},
{
"id": "ce47053b-6d6f-42b0-9c75-4182b49d52ed",
"name": "end_time",
"type": "string",
"value": "={{ $json.body.message.toolCallList[0].function.arguments.endTime }}"
},
{
"id": "0ad396c0-d140-4eab-a282-9380d3f20225",
"name": "title",
"type": "string",
"value": "={{ $json.body.message.toolCallList[0].function.arguments.title }}"
},
{
"id": "fe43abe3-43bc-4c12-bfe2-926d9e54b069",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number || \"0\" }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e5c5094a-8865-49c0-ab41-38f4bc4096b4",
"name": "Is Booking Data Valid?",
"type": "n8n-nodes-base.if",
"position": [
-192,
144
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c621a08c-cd1d-4aa3-989b-fc5e229021a5",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ ($json.email).isEmail() }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "8f9972c1-e597-4366-be88-865c7484f35f",
"name": "Format Calendar Payload",
"type": "n8n-nodes-base.code",
"position": [
256,
48
],
"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.start_time;\n const endTimeUTC = item.json.end_time;\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": "1ca152c9-7551-4bcf-96bf-63ddab2fa3fe",
"name": "Clean Data",
"type": "n8n-nodes-base.code",
"position": [
32,
48
],
"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;"
},
"typeVersion": 2
},
{
"id": "c1866014-67dd-4ef4-b8ec-9a20e1574b3e",
"name": "Create Calendar Event",
"type": "n8n-nodes-base.googleCalendar",
"onError": "continueErrorOutput",
"position": [
480,
48
],
"parameters": {
"end": "={{ $json.end_time }}",
"start": "={{ $json.start_time }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
},
"additionalFields": {
"allday": "no",
"summary": "={{ $json.title }}",
"showMeAs": "opaque",
"attendees": [
"={{ $json.email }}"
],
"description": "={{ $json.notes }}",
"sendUpdates": "all",
"conferenceDataUi": {
"conferenceDataValues": {
"conferenceSolution": "hangoutsMeet"
}
}
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "3b5d3d7d-d85d-4abd-b563-c26afb1dc7e4",
"name": "Normalize Data",
"type": "n8n-nodes-base.set",
"position": [
704,
-48
],
"parameters": {
"options": {
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "3386e288-f57e-41da-8ec3-71f3ae78beab",
"name": "id",
"type": "string",
"value": "={{ $json.id }}"
},
{
"id": "77767b18-9ad1-41b6-b9c4-76bfba1030a3",
"name": "description",
"type": "string",
"value": "={{ $json.description }}"
},
{
"id": "cc5820a0-bf39-44e7-a946-9444f6bc3921",
"name": "start_time",
"type": "string",
"value": "={{ $json.start.dateTime }}"
},
{
"id": "09a7eee5-e80b-4080-a9b7-f389e70261a6",
"name": "end_time",
"type": "string",
"value": "={{ $json.end.dateTime }}"
},
{
"id": "b4260904-0dd8-4bd9-a41d-9096f0272ee4",
"name": "timezone",
"type": "string",
"value": "={{ $json.start.timeZone }}"
},
{
"id": "52c652d6-098b-436c-bcc1-144520963191",
"name": "status",
"type": "string",
"value": "={{ $json.status }}"
},
{
"id": "2a203a60-1438-451d-bf26-bcd7c76cabb0",
"name": "attendees",
"type": "array",
"value": "={{ $json.attendees }}"
},
{
"id": "38b67807-3719-4346-b1d1-491a946cf8cc",
"name": "meeting_link",
"type": "string",
"value": "={{ $json.hangoutLink }}"
},
{
"id": "fb1bcfbd-b6f9-4058-965a-c1d583cb547f",
"name": "title",
"type": "string",
"value": "={{ $json.summary }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "23316c6b-7e68-49c5-acd7-0c5e43a7ddfe",
"name": "Build Success Response Body",
"type": "n8n-nodes-base.set",
"position": [
928,
-48
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fa724638-6251-46ef-af94-f305323f51ee",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Booking Data').item.json.call_id }}"
},
{
"id": "4b1b7c71-2d04-4286-9160-629330301c47",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.status }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d993c099-a7bf-4785-a016-a5d1f8a5ccb5",
"name": "Return Booking Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1152,
48
],
"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.4
},
{
"id": "021743e2-cbc2-4604-99ed-985848e79939",
"name": "Pass Successful Records Only",
"type": "n8n-nodes-base.filter",
"position": [
1376,
48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "4334dede-d42d-41c8-b887-c54020f731e7",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.results[0].result }}",
"rightValue": "confirmed"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "70ef7fd6-cd57-4e1c-8eea-f7ebc4cb8c63",
"name": "Prepare Airtable Record",
"type": "n8n-nodes-base.set",
"position": [
1600,
48
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d3396d54-31e6-4a14-a79e-7698eeff60d2",
"name": "start_time",
"type": "string",
"value": "={{ $('Normalize Data').item.json.start_time }}"
},
{
"id": "907c6d98-fa1d-4e5a-9aab-63e4599de665",
"name": "end_time",
"type": "string",
"value": "={{ $('Normalize Data').item.json.end_time }}"
},
{
"id": "7f75d9cd-42ae-4a71-beda-5adad67140f6",
"name": "status",
"type": "string",
"value": "={{ $('Normalize Data').item.json.status }}"
},
{
"id": "d86a419b-ab6f-4b4b-9272-412e84d39ef0",
"name": "timezone",
"type": "string",
"value": "={{ $('Normalize Data').item.json.timezone }}"
},
{
"id": "3f875d2b-90b5-42cc-9def-62b4dfb14433",
"name": "description",
"type": "string",
"value": "={{ $('Normalize Data').item.json.description }}"
},
{
"id": "ea77183d-8c62-428a-8287-01eb33748bc6",
"name": "response_status",
"type": "string",
"value": "={{ $('Normalize Data').item.json.attendees[0].responseStatus }}"
},
{
"id": "f8a24b6f-d6fe-49be-9fd2-06ec61afdb65",
"name": "email",
"type": "string",
"value": "={{ $('Normalize Data').item.json.attendees[0].email }}"
},
{
"id": "4b6e6b9b-39e5-4e48-9bc3-9787e89f191a",
"name": "meeting_link",
"type": "string",
"value": "={{ $('Normalize Data').item.json.meeting_link }}"
},
{
"id": "af256323-36c3-4d3d-95d3-4084ee118a1a",
"name": "attendee_name",
"type": "string",
"value": "={{ $('Incoming Booking Request').item.json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "cfc79c3c-fb3f-46d0-b33f-ce3ea5053d24",
"name": "call_id",
"type": "string",
"value": "={{ $('Incoming Booking Request').item.json.body.message.toolCalls[0].id }}"
},
{
"id": "ee23f152-4c7d-4bd3-a958-3ebfafc552e8",
"name": "assistant_name",
"type": "string",
"value": "={{ $('Incoming Booking Request').item.json.body.message.assistant.name }}"
},
{
"id": "18eb352c-4346-4442-a6ad-a82316c85ed2",
"name": "event_id",
"type": "string",
"value": "={{ $('Normalize Data').item.json.id }}"
},
{
"id": "7fc3b18d-6926-4af9-92be-8e813fd89206",
"name": "title",
"type": "string",
"value": "={{ $('Normalize Data').item.json.title }}"
},
{
"id": "86c5d917-cc0e-4cbb-a980-e1ee741dd4e6",
"name": "customer_number",
"type": "string",
"value": "={{ $('Normalize Booking Data').item.json.customer_number }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "99e74d55-823c-4f63-8c22-3e985fca1930",
"name": "Create Booking Record in Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
1824,
48
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"columns": {
"value": {
"Name": "={{ $json.attendee_name }}",
"Email": "={{ $json.email }}",
"endtime": "={{ $json.end_time }}",
"eventId": "={{ $json.event_id }}",
"meetlink": "={{ $json.meeting_link }}",
"starttime": "={{ $json.start_time }}",
"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": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"typecast": true
},
"operation": "create",
"authentication": "airtableOAuth2Api"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1
},
{
"id": "4b491656-bb76-40e7-a377-f277afcfd5ed",
"name": "Build Error Response",
"type": "n8n-nodes-base.set",
"position": [
32,
240
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0ee7bcad-4c45-4cff-a91e-27f0ce94192c",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Booking Data').item.json.call_id }}"
},
{
"id": "c45bcc46-673a-43e4-9fb4-8fb932977085",
"name": "results[0].result",
"type": "string",
"value": "You must provide an email, name, and notes to call this tool."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "98b27e17-47e0-4fbd-a54f-b93821b75858",
"name": "Return Booking Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
256,
240
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"message\": \"Booking failed!\"\n}"
},
"typeVersion": 1.4
},
{
"id": "62a5104f-f8de-4058-ba74-de252b75d8e4",
"name": "Build Calendar Error Details",
"type": "n8n-nodes-base.code",
"position": [
704,
144
],
"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": "c58ac69f-e7e0-428b-b8b4-860eaee0c5f8",
"name": "Build Calendar Failure Response",
"type": "n8n-nodes-base.set",
"position": [
928,
144
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0ee7bcad-4c45-4cff-a91e-27f0ce94192c",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Booking Data').item.json.call_id }}"
},
{
"id": "c45bcc46-673a-43e4-9fb4-8fb932977085",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.message || $json.status }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "688c7245-d747-49e9-8555-055a6d87265b",
"name": "Incoming Update Request",
"type": "n8n-nodes-base.webhook",
"position": [
-640,
640
],
"webhookId": "78655922-fba5-4842-a31c-2e9bf79f0dce",
"parameters": {
"path": "updateslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "cca08be3-35d9-4b9a-a8e7-bdaa5ecaaf6d",
"name": "Normalize Update Data",
"type": "n8n-nodes-base.set",
"position": [
-416,
640
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "aa5f0dd4-2d50-4ccc-9298-0197a1dd58b7",
"name": "id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "faf85861-fc1b-4e40-a62c-66b61060d809",
"name": "call_id",
"type": "string",
"value": "={{ $json.body.message.call.id }}"
},
{
"id": "0aef8183-ad42-494f-a0db-5c86fb40c7f4",
"name": "name",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "b41d2f4a-ced5-477a-a341-cf87f5f26190",
"name": "email",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.email }}"
},
{
"id": "d2998b59-af4e-4d50-8da3-0d2763e1fb7f",
"name": "start_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.startTime }}"
},
{
"id": "f90fa0c5-792d-4c08-8737-a501b686c1c8",
"name": "end_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.endTime }}"
},
{
"id": "c82a2ecf-0c19-4d89-ba70-fd58798deae7",
"name": "rescheduled_start_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.rescheduled_startTime }}"
},
{
"id": "9a343aee-d610-4676-82fd-a0e70a16b9cc",
"name": "rescheduled_end_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.rescheduled_endTime }}"
},
{
"id": "0afdf705-58c0-4f86-b636-9551991862ec",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1c1aa3ae-e7c2-4738-a00b-99b75adb8dfb",
"name": "Is Update Data Valid?",
"type": "n8n-nodes-base.if",
"position": [
-192,
640
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "709f803f-cfdf-4263-bbce-48bdec041612",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.name }}",
"rightValue": ""
},
{
"id": "6f27455b-d8fd-4bd2-9301-88a7bd232a61",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
},
{
"id": "911beddb-7f5e-4e0a-b18f-59ae9c372195",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.start_time }}",
"rightValue": ""
},
{
"id": "9d2eec70-ec72-4807-bb1e-203bea46d78f",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.end_time }}",
"rightValue": ""
},
{
"id": "90854df8-02eb-432a-ac9c-9a7ab691ae26",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.rescheduled_start_time }}",
"rightValue": ""
},
{
"id": "05521981-a452-4ef7-a6ca-ad5c9d1d9eaa",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.rescheduled_end_time }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ee610eca-2350-429c-a106-40493ef20a6c",
"name": "Find Booking Record",
"type": "n8n-nodes-base.airtable",
"position": [
32,
544
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"options": {
"fields": [
"Email",
"Name",
"starttime",
"eventId",
"endtime"
]
},
"operation": "search",
"authentication": "airtableOAuth2Api",
"filterByFormula": "={Phone Number} = (\"{{$json.customer_number}}\")"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1
},
{
"id": "5218f41e-c241-438c-bff6-7583a3606ae2",
"name": "Update Calendar Event",
"type": "n8n-nodes-base.googleCalendar",
"onError": "continueErrorOutput",
"position": [
256,
544
],
"parameters": {
"eventId": "={{ $json.eventId }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
},
"operation": "update",
"updateFields": {
"end": "={{ $('Normalize Update Data').item.json.rescheduled_end_time }}",
"start": "={{ $('Normalize Update Data').item.json.rescheduled_start_time }}",
"sendUpdates": "all"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "2c8f08ee-e475-4499-9eed-7d1da745e6c7",
"name": "Update Airtable Booking Record",
"type": "n8n-nodes-base.airtable",
"position": [
480,
432
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"columns": {
"value": {
"endtime": "={{ $json.end.dateTime }}",
"eventId": "={{ $('Find Booking Record').item.json.eventId }}",
"starttime": "={{ $json.start.dateTime }}",
"Booking Status": "Rescheduled"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone Number",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Phone Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": true,
"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": true,
"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": true,
"readOnly": false,
"required": false,
"displayName": "meetlink",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meetdescription",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "meetdescription",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Agent",
"type": "array",
"display": true,
"removed": true,
"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": [
"eventId"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"authentication": "airtableOAuth2Api"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1
},
{
"id": "42385114-7fc2-4d8b-91b1-57d6bb4c230b",
"name": "Build Update Message",
"type": "n8n-nodes-base.set",
"position": [
704,
560
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "70ca59ac-4db1-469b-8f5d-5939e03129ce",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Incoming Update Request').item.json.body.message.toolCalls[0].id }}"
},
{
"id": "557323be-8653-4611-b9fb-cff7b4c1f4e9",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.error || $json.fields['Booking Status'] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "47031451-bde8-48ab-8d91-568794587c32",
"name": "Return Update Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
928,
560
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $json.results[0].toolCallId }}\",\n \"result\":\"{{ $json.results[0].result }}\"\n }\n ]\n}"
},
"typeVersion": 1.4
},
{
"id": "a4b83a82-2995-4ebb-acf8-8eaf3320e26a",
"name": "Build Update Error Message",
"type": "n8n-nodes-base.set",
"position": [
32,
736
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cd137036-090a-46d7-bd26-d8c929e51f48",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Update Data').item.json.call_id || $json.Calls[0].id}}"
},
{
"id": "672d9837-5b78-41a3-9734-8a92564e7f51",
"name": "results[0].result",
"type": "string",
"value": "You must provide an email, name, previous start time & end time, and rescheduled start time to call this tool."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "84a722c8-fa57-4d7c-987d-9e692eb56592",
"name": "Return Update Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
256,
736
],
"parameters": {
"options": {}
},
"typeVersion": 1.4
},
{
"id": "380f7c5c-d244-4918-a2cf-b0f27e51f357",
"name": "Incoming Cancellation Request",
"type": "n8n-nodes-base.webhook",
"position": [
-640,
1120
],
"webhookId": "6ea7556c-f2d4-4e0c-aeeb-3aae5ab703a3",
"parameters": {
"path": "cancelslots",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "2f0d98de-c284-4ae9-955c-d8881c1a4314",
"name": "Normalize Cancellation Data",
"type": "n8n-nodes-base.set",
"position": [
-416,
1120
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "03ebdc90-a798-42da-b3a0-9c31559d001e",
"name": "id",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].id }}"
},
{
"id": "f4d875a8-5052-46f9-9a94-91797d8a98b3",
"name": "name",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.name }}"
},
{
"id": "99d9fe8e-63b4-4d26-b671-2fd28ffcefbe",
"name": "email",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.email }}"
},
{
"id": "83abb1e3-ead1-4869-a57c-a674d7a40537",
"name": "start_time",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.startTime }}"
},
{
"id": "411a649e-afd6-4024-b16c-8376d317ee67",
"name": "cancel_notes",
"type": "string",
"value": "={{ $json.body.message.toolCalls[0].function.arguments.cancelNotes }}"
},
{
"id": "c7a83c54-5d12-4674-a1c4-04032830b634",
"name": "call_id",
"type": "string",
"value": "={{ $json.body.message.call.id }}"
},
{
"id": "a3318276-8162-477b-ad13-4ffb19f79d65",
"name": "customer_number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4e2e6462-836b-490e-ba17-0680af00f1fc",
"name": "Is Cancellation Data Valid?",
"type": "n8n-nodes-base.if",
"position": [
-192,
1120
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "29beb8b4-96e1-4386-bf0c-7ce920599bed",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.name }}",
"rightValue": ""
},
{
"id": "cc8bfbbd-c60e-4993-9d56-3d9bb07c3fba",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
},
{
"id": "e1211d46-a59d-43a1-a6fd-242ac75ed0ac",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.start_time }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "8bc3e0ad-c28d-4f94-9e1a-801c5e8bdde1",
"name": "Find Booking Record to Cancel",
"type": "n8n-nodes-base.airtable",
"position": [
32,
1024
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"options": {
"fields": [
"Email",
"Name",
"starttime",
"endtime",
"eventId"
]
},
"operation": "search",
"authentication": "airtableOAuth2Api",
"filterByFormula": "={Phone Number} = (\"{{$json.customer_number}}\")"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "4fef7792-d643-40a9-ad06-6fe945b047e3",
"name": "Delete Calendar Event",
"type": "n8n-nodes-base.googleCalendar",
"onError": "continueErrorOutput",
"position": [
256,
1024
],
"parameters": {
"eventId": "={{ $json.eventId }}",
"options": {
"sendUpdates": "all"
},
"calendar": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
},
"operation": "delete"
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "ecf9696d-f106-4b69-83dc-f7170d1dfcb4",
"name": "Mark Booking as Cancelled",
"type": "n8n-nodes-base.airtable",
"position": [
480,
912
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblF8LF9lmkHMbk7v",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tblF8LF9lmkHMbk7v",
"cachedResultName": "Appointments"
},
"columns": {
"value": {
"eventId": "={{ $('Find Booking Record to Cancel').item.json.eventId }}",
"Booking Status": "Canceled"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone Number",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Phone Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": true,
"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": true,
"readOnly": false,
"required": false,
"displayName": "CallRecordingId",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "starttime",
"type": "dateTime",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "starttime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "endtime",
"type": "dateTime",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "endtime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meetlink",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "meetlink",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meetdescription",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "meetdescription",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Agent",
"type": "array",
"display": true,
"removed": true,
"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": [
"eventId"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"authentication": "airtableOAuth2Api"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1
},
{
"id": "d3aaf75b-b3c1-4412-bd2a-af4a9a2eb4d5",
"name": "Build Cancellation Message",
"type": "n8n-nodes-base.set",
"position": [
704,
1040
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "675235d2-0d3b-446c-922a-27a1b200e299",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Cancellation Data').item.json.id }}"
},
{
"id": "b227598e-3a16-4a98-9913-66ea4ad53e11",
"name": "results[0].result",
"type": "string",
"value": "={{ $json.error || $json.fields['Booking Status'] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dede653d-78f1-4f31-ab05-f6226371a326",
"name": "Return Cancellation Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
928,
1040
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"results\":[\n {\n \"toolCallId\":\"{{ $json.results[0].toolCallId }}\",\n \"result\":\"{{ $json.results[0].result }}\"\n }\n ]\n}"
},
"typeVersion": 1.4
},
{
"id": "10f1e5d7-8b5f-4687-9e05-ccb04cded73c",
"name": "Build Cancellation Error Message",
"type": "n8n-nodes-base.set",
"position": [
32,
1216
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cd137036-090a-46d7-bd26-d8c929e51f48",
"name": "results[0].toolCallId",
"type": "string",
"value": "={{ $('Normalize Cancellation Data').item.json.call_id || $json.Calls[0].id}}"
},
{
"id": "672d9837-5b78-41a3-9734-8a92564e7f51",
"name": "results[0].result",
"type": "string",
"value": "You must provide an email, name, and start time to call this tool."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "09c143ba-73b2-4a20-a1f6-a359b61a7b20",
"name": "Return Cancellation Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
256,
1216
],
"parameters": {
"options": {}
},
"typeVersion": 1.4
},
{
"id": "b5fa5c6d-957f-43bf-83a1-cc5f4714fdb4",
"name": "Incoming Call Result",
"type": "n8n-nodes-base.webhook",
"position": [
-640,
1440
],
"webhookId": "4c3526f1-01c5-48f2-b707-f932fc0578be",
"parameters": {
"path": "callresults",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2.1
},
{
"id": "990dcb80-b113-46ee-accb-570fb3a1c249",
"name": "Normalize Call Result Data",
"type": "n8n-nodes-base.set",
"position": [
-416,
1440
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f6ed0324-f650-43d2-a9a7-21c4d724f146",
"name": "transcript",
"type": "string",
"value": "={{ $json.body.message.artifact.transcript }}"
},
{
"id": "01103ef4-b827-41e2-b470-62db65efef75",
"name": "recordingUrl",
"type": "string",
"value": "={{ $json.body.message.artifact.recordingUrl }}"
},
{
"id": "9fd2d638-dff8-44b8-b02b-832a63458f85",
"name": "call_summary",
"type": "string",
"value": "={{ $json.body.message.analysis.summary }}"
},
{
"id": "90f07f50-b69a-40b1-a9f2-82c07b01189a",
"name": "cost",
"type": "number",
"value": "={{ $json.body.message.cost }}"
},
{
"id": "ea6b51f5-4d8f-41d9-8991-e459683b0235",
"name": "call_id",
"type": "string",
"value": "={{ $json.body.message.call.id }}"
},
{
"id": "2e787a98-187e-45b7-b12c-c90f66838625",
"name": "call_org_id",
"type": "string",
"value": "={{ $json.body.message.call.orgId }}"
},
{
"id": "c6fe60e0-27cd-43a2-b07f-83000a0f42ec",
"name": "assistant_id",
"type": "string",
"value": "={{ $json.body.message.assistant.id }}"
},
{
"id": "53e83ca0-5b68-4b54-b49e-d1dd3a7bc93a",
"name": "assistant_name",
"type": "string",
"value": "={{ $json.body.message.assistant.name }}"
},
{
"id": "3a19249f-5995-4828-bf78-0228cd42efd6",
"name": "assistant_model",
"type": "string",
"value": "={{ $json.body.message.assistant.model.model }}"
},
{
"id": "5c407b1c-08ed-4fcb-8ebb-7cf63d6be9a5",
"name": "startedAt",
"type": "string",
"value": "={{ $json.body.message.startedAt }}"
},
{
"id": "4d257127-9033-4002-95ea-d60e012515de",
"name": "endedAt",
"type": "string",
"value": "={{ $json.body.message.endedAt }}"
},
{
"id": "ccf7f0ef-fdbb-47e3-bfdd-8ca3afb7c2cf",
"name": "body_message_assistant",
"type": "object",
"value": "={{ $json.body.message.assistant }}"
},
{
"id": "b533fb1f-8a62-46c4-850b-e0cade2a621f",
"name": "assistantId",
"type": "string",
"value": "={{ $json.body.message.call.assistantId }}"
},
{
"id": "eee4f9b1-3693-40b1-80ff-91ebe60dcb93",
"name": "body.message.assistant.model.emotionRecognitionEnabled",
"type": "boolean",
"value": "={{ $json.body.message.assistant.model.emotionRecognitionEnabled }}"
},
{
"id": "97d8d76a-5353-4d9d-afe1-016d8dd7b829",
"name": "customer.number",
"type": "string",
"value": "={{ $json.body.message.call.customer.number }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "608c7fea-89ba-46d9-91f2-0e472b5e095f",
"name": "Upsert Call Result in Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
-192,
1440
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app0hmfghmfh7BiAx",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx",
"cachedResultName": "AI Appointment Setter Build"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tbl1b6vMhq9IT9JEZ",
"cachedResultUrl": "https://airtable.com/app0hmfghmfh7BiAx/tbl1b6vMhq9IT9JEZ",
"cachedResultName": "Call Recording"
},
"columns": {
"value": {
"Cost": "={{ $json.cost }}",
"endedAt": "={{ $json.endedAt }}",
"startedAt": "={{ $json.startedAt }}",
"transcript": "={{ $json.transcript }}",
"callsummary": "={{ $json.call_summary }}",
"customer_Number": "={{ $json.customer.number }}",
"callrecording_id": "={{ $json.call_id }}",
"Call recording Url": "={{ $json.recordingUrl }}"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "callrecording_id",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "callrecording_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Cost",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Cost",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Call recording Url",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Call recording Url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "transcript",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "transcript",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "customer_Number",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "customer_Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Appointments",
"type": "array",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Appointments",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Appointment time (from Appointments)",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Appointment time (from Appointments)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Agent (from Appointments)",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Voice Agent (from Appointments)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "startedAt",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "startedAt",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "endedAt",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "endedAt",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "call_Length (in secs)",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "call_Length (in secs)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "callsummary",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "callsummary",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"callrecording_id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "upsert",
"authentication": "airtableOAuth2Api"
},
"credentials": {
"airtableOAuth2Api": {
"id": "Pm127r9qcbA3zqiw",
"name": "Airtable account"
}
},
"typeVersion": 2.1
},
{
"id": "cb850ab8-5a43-49c9-ba27-5fb1814fbed8",
"name": "On New Calendar Event",
"type": "n8n-nodes-base.googleCalendarTrigger",
"position": [
48,
1440
],
"parameters": {
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyHour",
"minute": 5
}
]
},
"triggerOn": "eventCreated",
"calendarId": {
"__rl": true,
"mode": "list",
"value": "dfcd9db322f05aa99f884482a53c650cc99755a77e23ff7c4d3730c3f0038b4f@group.calendar.google.com",
"cachedResultName": "Winston Labs Booking"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "wmEBz98I4aRuq2FO",
"name": "Google Calendar account"
}
},
"typeVersion": 1
},
{
"id": "81766121-b077-41fa-a6b2-7be7553cd4fa",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-528
],
"parameters": {
"color": 3,
"width": 2752,
"height": 464,
"content": "## Availability Request"
},
"typeVersion": 1
},
{
"id": "8983e730-593d-412f-849b-890a2134db53",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-64
],
"parameters": {
"color": 4,
"width": 2752,
"height": 464,
"content": "## Booking Request"
},
"typeVersion": 1
},
{
"id": "846659ee-3790-4cf8-8fe8-9ee340ac508b",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
400
],
"parameters": {
"color": 5,
"width": 2752,
"height": 496,
"content": "## Update Request"
},
"typeVersion": 1
},
{
"id": "98f9c093-fff6-4891-a679-21d73a6dc837",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
896
],
"parameters": {
"color": 6,
"width": 2752,
"height": 496,
"content": "## Cancellation Request"
},
"typeVersion": 1
},
{
"id": "c6e92deb-c96b-405a-b401-116724310ab7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
1392
],
"parameters": {
"width": 688,
"height": 240,
"content": "## Call Results"
},
"typeVersion": 1
},
{
"id": "b7460aec-6fd1-492b-8bb3-e4710d221389",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
1392
],
"parameters": {
"color": 7,
"width": 688,
"height": 240,
"content": "## Notification"
},
"typeVersion": 1
},
{
"id": "e95609f3-8026-40d7-81be-097fc9c00984",
"name": "Send Event Notification Email",
"type": "n8n-nodes-base.gmail",
"position": [
256,
1440
],
"webhookId": "4c87b726-0137-40a0-932d-b64432935e37",
"parameters": {
"sendTo": "kwcollantes@gmail.com",
"message": "New Booking at Google Calendar",
"options": {},
"subject": "New Booking at Google Calendar",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "GyMmkOoY1Z4um24d",
"name": "Gmail account"
}
},
"typeVersion": 2.1
}
],
"active": true,
"pinData": {},
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": "9Y40hngg1PkXFs6I",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "b1f50972-516e-4ae6-b94a-ef92b2d6b6c5",
"connections": {
"Aggregate": {
"main": [
[
{
"node": "Assemble Availability Payload",
"type": "main",
"index": 0
}
]
]
},
"Clean Data": {
"main": [
[
{
"node": "Format Calendar Payload",
"type": "main",
"index": 0
}
]
]
},
"Normalize Data": {
"main": [
[
{
"node": "Build Success Response Body",
"type": "main",
"index": 0
}
]
]
},
"Find Booking Record": {
"main": [
[
{
"node": "Update Calendar Event",
"type": "main",
"index": 0
}
]
]
},
"Build Error Response": {
"main": [
[
{
"node": "Return Booking Error Response",
"type": "main",
"index": 0
}
]
]
},
"Build Update Message": {
"main": [
[
{
"node": "Return Update Response",
"type": "main",
"index": 0
}
]
]
},
"Incoming Call Result": {
"main": [
[
{
"node": "Normalize Call Result Data",
"type": "main",
"index": 0
}
]
]
},
"Create Calendar Event": {
"main": [
[
{
"node": "Normalize Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Calendar Error Details",
"type": "main",
"index": 0
}
]
]
},
"Delete Calendar Event": {
"main": [
[
{
"node": "Mark Booking as Cancelled",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Cancellation Message",
"type": "main",
"index": 0
}
]
]
},
"Fetch Calendar Events": {
"main": [
[
{
"node": "Format Events for Output",
"type": "main",
"index": 0
}
]
]
},
"Is Update Data Valid?": {
"main": [
[
{
"node": "Find Booking Record",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Update Error Message",
"type": "main",
"index": 0
}
]
]
},
"Normalize Update Data": {
"main": [
[
{
"node": "Is Update Data Valid?",
"type": "main",
"index": 0
}
]
]
},
"On New Calendar Event": {
"main": [
[
{
"node": "Send Event Notification Email",
"type": "main",
"index": 0
}
]
]
},
"Update Calendar Event": {
"main": [
[
{
"node": "Update Airtable Booking Record",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Update Message",
"type": "main",
"index": 0
}
]
]
},
"Check Slot in Calendar": {
"main": [
[
{
"node": "Is Requested Slot Free?",
"type": "main",
"index": 0
}
]
]
},
"Is Booking Data Valid?": {
"main": [
[
{
"node": "Clean Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Error Response",
"type": "main",
"index": 0
}
]
]
},
"Normalize Booking Data": {
"main": [
[
{
"node": "Is Booking Data Valid?",
"type": "main",
"index": 0
}
]
]
},
"Normalize Request Data": {
"main": [
[
{
"node": "Check Slot in Calendar",
"type": "main",
"index": 0
}
]
]
},
"Format Calendar Payload": {
"main": [
[
{
"node": "Create Calendar Event",
"type": "main",
"index": 0
}
]
]
},
"Incoming Update Request": {
"main": [
[
{
"node": "Normalize Update Data",
"type": "main",
"index": 0
}
]
]
},
"Is Requested Slot Free?": {
"main": [
[
{
"node": "Return Successful Availability Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Fetch Calendar Events",
"type": "main",
"index": 0
}
]
]
},
"Prepare Airtable Record": {
"main": [
[
{
"node": "Create Booking Record in Airtable",
"type": "main",
"index": 0
}
]
]
},
"Return Booking Response": {
"main": [
[
{
"node": "Pass Successful Records Only",
"type": "main",
"index": 0
}
]
]
},
"Format Events for Output": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"Incoming Booking Request": {
"main": [
[
{
"node": "Normalize Booking Data",
"type": "main",
"index": 0
}
]
]
},
"Mark Booking as Cancelled": {
"main": [
[
{
"node": "Build Cancellation Message",
"type": "main",
"index": 0
}
]
]
},
"Build Cancellation Message": {
"main": [
[
{
"node": "Return Cancellation Response",
"type": "main",
"index": 0
}
]
]
},
"Build Update Error Message": {
"main": [
[
{
"node": "Return Update Error Response",
"type": "main",
"index": 0
}
]
]
},
"Normalize Call Result Data": {
"main": [
[
{
"node": "Upsert Call Result in Airtable",
"type": "main",
"index": 0
}
]
]
},
"Build Success Response Body": {
"main": [
[
{
"node": "Return Booking Response",
"type": "main",
"index": 0
}
]
]
},
"Is Cancellation Data Valid?": {
"main": [
[
{
"node": "Find Booking Record to Cancel",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Cancellation Error Message",
"type": "main",
"index": 0
}
]
]
},
"Normalize Cancellation Data": {
"main": [
[
{
"node": "Is Cancellation Data Valid?",
"type": "main",
"index": 0
}
]
]
},
"Build Calendar Error Details": {
"main": [
[
{
"node": "Build Calendar Failure Response",
"type": "main",
"index": 0
}
]
]
},
"Pass Successful Records Only": {
"main": [
[
{
"node": "Prepare Airtable Record",
"type": "main",
"index": 0
}
]
]
},
"Assemble Availability Payload": {
"main": [
[
{
"node": "Return Available Slots",
"type": "main",
"index": 0
}
]
]
},
"Find Booking Record to Cancel": {
"main": [
[
{
"node": "Delete Calendar Event",
"type": "main",
"index": 0
}
]
]
},
"Incoming Availability Request": {
"main": [
[
{
"node": "Normalize Request Data",
"type": "main",
"index": 0
}
]
]
},
"Incoming Cancellation Request": {
"main": [
[
{
"node": "Normalize Cancellation Data",
"type": "main",
"index": 0
}
]
]
},
"Update Airtable Booking Record": {
"main": [
[
{
"node": "Build Update Message",
"type": "main",
"index": 0
}
]
]
},
"Build Calendar Failure Response": {
"main": [
[
{
"node": "Return Booking Response",
"type": "main",
"index": 0
}
]
]
},
"Build Cancellation Error Message": {
"main": [
[
{
"node": "Return Cancellation Error Response",
"type": "main",
"index": 0
}
]
]
},
"Return Successful Availability Response": {
"main": [
[]
]
}
}
}