{
  "name": "No-show handler: confirm, reschedule or cancel a meeting by email",
  "nodes": [
    {
      "id": "d0787850-165b-4f58-943b-bc4e3862334b",
      "name": "Read confirm request",
      "type": "n8n-nodes-base.set",
      "position": [
        4016,
        -64
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n \"action\": \"{{$json[\"query\"][\"action\"]}}\",\n \"email\": \"{{$json[\"query\"][\"email\"]}}\",\n \"eventId\": \"{{$json[\"query\"][\"eventId\"]}}\"\n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "7d62f462-a945-4695-bf68-e49a318aad2d",
      "name": "Read event details (confirm)",
      "type": "n8n-nodes-base.set",
      "position": [
        4576,
        -64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "77b5995b-4fc8-4de1-90d6-ff3f0bd98650",
              "name": "email",
              "type": "string",
              "value": "={{ ($json.attendees || []).find(a => !a.organizer)?.email || ($json.attendees || [])[0]?.email }}"
            },
            {
              "id": "be290c41-c125-4f77-94a1-b44aa5ac90cb",
              "name": "eventId",
              "type": "string",
              "value": "={{ $json[\"id\"] }}"
            },
            {
              "id": "b6aef243-fe5c-418a-aa4f-9cbfebe11455",
              "name": "meetingTime",
              "type": "string",
              "value": "={{ $json[\"start\"][\"dateTime\"] }}"
            },
            {
              "id": "2bf31ebc-cbc4-48be-b99c-50022954b175",
              "name": "meetingLink",
              "type": "string",
              "value": "={{ $json.hangoutLink }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1fbaea95-48fd-42ab-8a1c-276e87371c9c",
      "name": "Get the event (confirm)",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        4304,
        -64
      ],
      "parameters": {
        "eventId": "={{ $json.eventId }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "operation": "get"
      },
      "typeVersion": 1.3
    },
    {
      "id": "e80590cc-0b1f-4ef0-aff5-e14fb35064ea",
      "name": "Read reschedule request",
      "type": "n8n-nodes-base.set",
      "position": [
        4016,
        560
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1e922f16-4173-4b7e-9b53-3a67e90157e5",
              "name": "email",
              "type": "string",
              "value": "={{$json[\"query\"][\"email\"]}}"
            },
            {
              "id": "c977e745-6709-4cfe-870c-590ffe43b5e0",
              "name": "action",
              "type": "string",
              "value": "={{$json[\"query\"][\"action\"]}}"
            },
            {
              "id": "61af1085-0b77-4c22-a7bd-3021de0060d4",
              "name": "eventId",
              "type": "string",
              "value": "={{$json[\"query\"][\"eventId\"]}}"
            },
            {
              "id": "2702b9f9-2f28-4b8f-8c6c-f1ffbca29acb",
              "name": "newDateTime",
              "type": "string",
              "value": "={{$json[\"query\"][\"newDateTime\"]}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "20076e41-3004-41a0-943f-d9e752a1196e",
      "name": "Get the event (reschedule)",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        4304,
        560
      ],
      "parameters": {
        "eventId": "={{ $json.eventId }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "operation": "get"
      },
      "typeVersion": 1.3
    },
    {
      "id": "7b78cbc9-066a-4de0-abac-ea309a6fd90c",
      "name": "Read event details (reschedule)",
      "type": "n8n-nodes-base.set",
      "position": [
        4576,
        560
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "77b5995b-4fc8-4de1-90d6-ff3f0bd98650",
              "name": "email",
              "type": "string",
              "value": "={{ ($json.attendees || []).find(a => !a.organizer)?.email || ($json.attendees || [])[0]?.email }}"
            },
            {
              "id": "be290c41-c125-4f77-94a1-b44aa5ac90cb",
              "name": "eventId",
              "type": "string",
              "value": "={{ $json[\"id\"] }}"
            },
            {
              "id": "2bf31ebc-cbc4-48be-b99c-50022954b175",
              "name": "meetingLink",
              "type": "string",
              "value": "={{ $json.hangoutLink }}"
            },
            {
              "id": "adf40cb8-afc9-4bba-8717-1a2ccdfc910d",
              "name": "newDateTime",
              "type": "string",
              "value": "={{ $('Read reschedule request').item.json[\"newDateTime\"] }}"
            },
            {
              "id": "d14e2410-c34a-40c1-bdf5-a63323dc3bd8",
              "name": "meetingDate",
              "type": "string",
              "value": "={{ $json.start.dateTime.split('T')[0] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "bbda4776-a4fe-4a90-853c-e1c7b01d4a03",
      "name": "Receive the click",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2512,
        592
      ],
      "parameters": {
        "path": "meeting-reschedule",
        "options": {},
        "responseData": "allEntries",
        "responseMode": "lastNode"
      },
      "typeVersion": 2
    },
    {
      "id": "b16b1940-0098-46e0-adfd-8f08141707b4",
      "name": "Which action?",
      "type": "n8n-nodes-base.switch",
      "position": [
        2800,
        576
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "confirm",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "6bd3f9be-524c-479c-baa1-922cd20008c4",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{$json[\"query\"][\"action\"]}}",
                    "rightValue": "confirm"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "reschedule",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7bcf2582-e868-4658-aa7c-e3e835bdbb57",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{$json[\"query\"][\"action\"]}}",
                    "rightValue": "reschedule"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "cancel",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "afbeaf6a-030c-4b1d-99ab-22e531342346",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{$json[\"query\"][\"action\"]}}",
                    "rightValue": "cancel"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "d85bdeb1-4cc7-4cc2-a7dd-efe8a7eaa86d",
      "name": "Send confirmation email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4864,
        -64
      ],
      "parameters": {
        "sendTo": "={{$json[\"email\"]}}",
        "message": "=<p>Hi {{ $json[\"email\"] }},</p> <p>Your meeting has been <strong>successfully confirmed</strong>. We look forward to seeing you!</p> <p>Meeting ID: {{ $json[\"eventId\"] }}</p> <p>Join your meeting here: <a href=\"{{ $json[\"meetingLink\"] }}\">{{ $json[\"meetingLink\"] }}</a></p> <p>Thanks,<br>Your Scheduler Bot</p>",
        "options": {},
        "subject": "Meeting confirmed"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b73f1d8a-0fc3-434b-907d-1ab14a8bbc3c",
      "name": "Send reschedule email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4864,
        480
      ],
      "parameters": {
        "sendTo": "={{ $json[\"email\"] }}",
        "message": "=<p> <b>User:</b> {{ $json.email }}</p>\n\n<p> <b>Meeting ID:</b> {{ $json.eventId }}</p>\n\n<p> <b>New Scheduled Time:</b>\n {{ $('Read reschedule request').item.json[\"newDateTime\"] }}\n</p>\n\n<p> <b>Join Link:</b>\n <a href=\"{{ $json.meetingLink }}\" target=\"_blank\">\n {{ $json.meetingLink }}\n </a>\n</p>\n\n<hr>\n<p style=\"color:gray; font-size:13px;\">This is an automated reschedule notification from your system.</p>\n",
        "options": {},
        "subject": "Your meeting has been rescheduled"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9e896e5d-516b-4f2b-91ef-7a35bd27af00",
      "name": "Move the meeting",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4864,
        720
      ],
      "parameters": {
        "text": "You are a smart meeting assistant connected to Google Calendar.\n\nA user has requested to reschedule their meeting. Use the provided input to:\n- First create the new event\n- Then delete the old one using eventId\n\n---\n\n### Input (Dynamic JSON from user):\n```json\n{{ $json }}\nYour Tasks:\nExtract the following from the input:\n\nemail: the attendee's email\n\neventId: the ID of the old event to delete\n\nnewDateTime: time range (e.g. \"06:00 PM – 07:00 PM\")\n\nmeetingDate: the date for the rescheduled event (format: YYYY-MM-DD)\n\nIf meetingDate is not provided, use today’s date.\n\nParse the newDateTime:\n\nFormat: \"hh:mm AM/PM – hh:mm AM/PM\"\n\nCombine with meetingDate\n\nTime zone: YOUR_TIMEZONE\n\nConvert both start and end to UTC ISO 8601\n\nCreate a new calendar event:\n\nSummary: \"Rescheduled Meeting\"\n\nAttendee: use the provided email\n\nStart and End time: use the parsed UTC values\n\nEnable Google Meet (hangoutLink)\n\n After the new event is successfully created, delete the old event using:\n\ncalendar.deleteEvent\n\nUse the original eventId from the input\n\n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "ac0c4dd0-8fc4-4c41-996a-30eb913d68ea",
      "name": "Chat model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        4800,
        960
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "08f82acb-9064-49f8-b64a-5a42a5d80037",
      "name": "Delete old event tool",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        5168,
        960
      ],
      "parameters": {
        "eventId": "={{ $json.eventId }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "operation": "delete",
        "descriptionType": "manual",
        "toolDescription": "=Use this tool to delete an existing Google Calendar event.\n\n- Identify the event to remove by its unique `eventId` provided in the input.\n- Ensure you call this **only after** creating any replacement event (if rescheduling).\n- Do not delete the event unless the new event was successfully created.\n- Return a simple confirmation once deletion succeeds.\n"
      },
      "typeVersion": 1.3
    },
    {
      "id": "dc6c4747-b41e-4ee0-bee7-894a17d2988b",
      "name": "Create new event tool",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        4976,
        960
      ],
      "parameters": {
        "end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', `check my ai agent chat input than choose END time `, 'string') }}",
        "start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', `check my ai agent chat input than choose strting time `, 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "descriptionType": "manual",
        "toolDescription": "=Use this tool to create a new calendar event based on the provided input.\n\n- Use the attendee's email to invite them.\n- Use `newDateTime` (e.g. \"06:00 PM – 07:00 PM\") along with `meetingDate` (e.g. \"2025-06-30\") to schedule the event.\n- Time zone: YOUR_TIMEZONE\n- Convert the start and end times to UTC ISO 8601 format.\n- Title the event: \"Rescheduled Meeting\"\n- Enable Google Meet by setting `conferenceDataVersion` to 1.\n- The event should be created only after correct time parsing.\n",
        "additionalFields": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "7d8bc477-c773-4fc0-932b-106a4d7944e2",
      "name": "Read cancel request",
      "type": "n8n-nodes-base.set",
      "position": [
        4032,
        1392
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n \"action\": \"{{$json[\"query\"][\"action\"]}}\",\n \"email\": \"{{$json[\"query\"][\"email\"]}}\",\n \"eventId\": \"{{$json[\"query\"][\"eventId\"]}}\"\n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "01c1f7ef-3507-4724-9d98-56e0648caf0c",
      "name": "Read event details (cancel)",
      "type": "n8n-nodes-base.set",
      "position": [
        4592,
        1392
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "77b5995b-4fc8-4de1-90d6-ff3f0bd98650",
              "name": "email",
              "type": "string",
              "value": "={{ ($json.attendees || []).find(a => !a.organizer)?.email || ($json.attendees || [])[0]?.email }}"
            },
            {
              "id": "be290c41-c125-4f77-94a1-b44aa5ac90cb",
              "name": "eventId",
              "type": "string",
              "value": "={{ $json[\"id\"] }}"
            },
            {
              "id": "b6aef243-fe5c-418a-aa4f-9cbfebe11455",
              "name": "meetingTime",
              "type": "string",
              "value": "={{ $json[\"start\"][\"dateTime\"] }}"
            },
            {
              "id": "2bf31ebc-cbc4-48be-b99c-50022954b175",
              "name": "meetingLink",
              "type": "string",
              "value": "={{ $json.hangoutLink }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "856123a4-f0b0-4406-894a-6e2ab5b92acb",
      "name": "Get the event (cancel)",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        4320,
        1392
      ],
      "parameters": {
        "eventId": "={{ $json.eventId }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "operation": "get"
      },
      "typeVersion": 1.3
    },
    {
      "id": "c1d36cbe-c4ee-4434-abb3-0fb8ee46edbc",
      "name": "Send cancellation email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        5152,
        1392
      ],
      "parameters": {
        "sendTo": "={{$json[\"email\"]}}",
        "message": "=<p>Hi {{ $json[\"email\"] }},</p>\n\n<p>Your meeting has been <strong>successfully cancelled</strong>.</p>\n\n<p>Meeting ID: {{ $json[\"eventId\"] }}</p>\n\n<p>If this was a mistake or you’d like to reschedule, please contact us.</p>\n\n<p>Thanks,<br>Your Scheduler Bot</p>\n",
        "options": {},
        "subject": "Meeting cancelled"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9ce680d1-9ca4-4a97-a3fc-1b414715745e",
      "name": "Cancel the event",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        4880,
        1392
      ],
      "parameters": {
        "eventId": "={{ $json.eventId }}",
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CALENDAR_ID"
        },
        "operation": "delete"
      },
      "typeVersion": 1.3
    },
    {
      "id": "0c9b3921-883b-4fb3-ba5f-1578b0b9e0ab",
      "name": "Sticky Note - Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2432,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 628,
        "height": 488,
        "content": "## 1. Receive the click\nOne webhook backs all three buttons in your reminder email. The action in the link decides which branch runs: confirm, reschedule or cancel."
      },
      "typeVersion": 1
    },
    {
      "id": "a9cdf50e-5b7b-48f7-8184-1946db51f87a",
      "name": "Sticky Note - Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3952,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 1460,
        "height": 400,
        "content": "## 2. Confirm\nThe event is fetched by its ID, the guest email and meeting link are read from it, and a confirmation email goes back to the guest."
      },
      "typeVersion": 1
    },
    {
      "id": "4eae09a7-4b63-459e-a891-38db1e7b4061",
      "name": "Sticky Note - Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3952,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 1488,
        "height": 800,
        "content": "## 3. Reschedule\nThe guest's new time is read from the link. An agent creates the replacement event with Google Meet, then deletes the old one, and the guest is emailed the new details."
      },
      "typeVersion": 1
    },
    {
      "id": "b27e64d8-8ab0-44cd-9719-9d27e25f2511",
      "name": "Sticky Note - Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3968,
        1232
      ],
      "parameters": {
        "color": 7,
        "width": 1468,
        "height": 352,
        "content": "## 4. Cancel\nThe event is fetched, deleted from the calendar, and the guest gets a cancellation email."
      },
      "typeVersion": 1
    },
    {
      "id": "fb12b9f0-c06f-413e-9001-0994239bcb3b",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        96
      ],
      "parameters": {
        "width": 908,
        "height": 1162,
        "content": "# No-show handler: confirm, reschedule or cancel a meeting by email\n\nPut three buttons in your meeting reminder email and let guests confirm, reschedule or cancel with one click. Each button hits the same webhook with a different action, and the workflow updates your Google Calendar and emails the guest back. Cuts down no-shows without anyone chasing replies.\n\n### How it works\n1. A webhook receives the click. The link carries the action, the guest email, the event ID, and for reschedules a new time.\n2. A switch routes to one of three branches based on that action.\n3. Confirm: the event is fetched by ID, the guest email and Google Meet link are read from it, and a confirmation email is sent.\n4. Reschedule: an AI agent reads the requested time, creates a replacement event with Google Meet enabled, then deletes the original. The guest is emailed the new time and link.\n5. Cancel: the event is fetched, deleted from the calendar, and a cancellation email is sent.\n\n### Setup\n1. Connect your Google Calendar and Gmail accounts, and add your OpenAI API key.\n2. Replace YOUR_CALENDAR_ID on all five calendar nodes with the calendar these meetings live in.\n3. Replace YOUR_TIMEZONE in the agent prompt and the create event tool with your own timezone, for example Europe/London.\n4. Activate, then build your reminder email links as the webhook URL plus action, email, eventId and newDateTime as query parameters.\n\n### Customization\nEdit the three email bodies to match your branding, or change the new event title in the create event tool."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Chat model": {
      "ai_languageModel": [
        [
          {
            "node": "Move the meeting",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Which action?": {
      "main": [
        [
          {
            "node": "Read confirm request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Read reschedule request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Read cancel request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cancel the event": {
      "main": [
        [
          {
            "node": "Send cancellation email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive the click": {
      "main": [
        [
          {
            "node": "Which action?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read cancel request": {
      "main": [
        [
          {
            "node": "Get the event (cancel)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read confirm request": {
      "main": [
        [
          {
            "node": "Get the event (confirm)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create new event tool": {
      "ai_tool": [
        [
          {
            "node": "Move the meeting",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Delete old event tool": {
      "ai_tool": [
        [
          {
            "node": "Move the meeting",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Get the event (cancel)": {
      "main": [
        [
          {
            "node": "Read event details (cancel)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get the event (confirm)": {
      "main": [
        [
          {
            "node": "Read event details (confirm)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read reschedule request": {
      "main": [
        [
          {
            "node": "Get the event (reschedule)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get the event (reschedule)": {
      "main": [
        [
          {
            "node": "Read event details (reschedule)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read event details (cancel)": {
      "main": [
        [
          {
            "node": "Cancel the event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read event details (confirm)": {
      "main": [
        [
          {
            "node": "Send confirmation email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read event details (reschedule)": {
      "main": [
        [
          {
            "node": "Send reschedule email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Move the meeting",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}
