diff --git a/workflows/Schedule appointments from a booking form with Google Calendar and Gmail-13923/readme-13923.md b/workflows/Schedule appointments from a booking form with Google Calendar and Gmail-13923/readme-13923.md
new file mode 100644
index 000000000..1a884ef6c
--- /dev/null
+++ b/workflows/Schedule appointments from a booking form with Google Calendar and Gmail-13923/readme-13923.md
@@ -0,0 +1,622 @@
+Schedule appointments from a booking form with Google Calendar and Gmail
+
+https://n8nworkflows.xyz/workflows/schedule-appointments-from-a-booking-form-with-google-calendar-and-gmail-13923
+
+
+# Schedule appointments from a booking form with Google Calendar and Gmail
+
+# 1. Workflow Overview
+
+This workflow implements a self-hosted appointment booking system using **n8n**, **Google Calendar**, and **Gmail**. It exposes a public booking page, computes available time slots based on calendar occupancy, accepts appointment submissions, creates a calendar event, sends a confirmation email, and returns a branded success page.
+
+It has **two entry points on the same path** (`/booking`), differentiated by HTTP method:
+
+- **GET /booking** → displays the booking form with dynamically generated available slots
+- **POST /booking** → processes the submitted form and creates the appointment
+
+## 1.1 Booking Form Delivery
+
+This block serves the HTML booking interface. It fetches upcoming calendar events, calculates free 30-minute slots for the next 30 days, and renders a complete HTML form with date navigation and slot selection.
+
+## 1.2 Booking Submission Processing
+
+This block receives the submitted form payload, computes the meeting end time from the chosen start slot, creates the Google Calendar event, enriches the event data for email rendering, sends a Gmail confirmation email, and returns an HTML success response.
+
+## 1.3 Shared Design / Documentation Layer
+
+A sticky note documents the workflow purpose, explains the two-path structure, and gives setup instructions for credentials and customization.
+
+---
+
+# 2. Block-by-Block Analysis
+
+## 2.1 Booking Form Delivery
+
+### Overview
+
+This block handles the **GET** request used to display the booking page. It reads existing events from Google Calendar, derives open appointment slots, builds a full front-end HTML form, and returns that page through the webhook response node.
+
+### Nodes Involved
+
+- Webhook - Show Form
+- Get Calendar Events
+- Calculate Available Slots
+- Build HTML Form
+- Respond to Webhook
+
+---
+
+### Node: Webhook - Show Form
+
+- **Type and technical role:** `n8n-nodes-base.webhook`
+ Entry point for serving the booking form.
+- **Configuration choices:**
+ - Path: `booking`
+ - Response mode: `responseNode`
+ - HTTP method not explicitly set, so this webhook is intended for the default form-serving route; in context it acts as the **GET handler**
+- **Key expressions or variables used:** none
+- **Input and output connections:**
+ - No input
+ - Output → `Get Calendar Events`
+- **Version-specific requirements:** `typeVersion: 1.1`
+- **Edge cases or potential failure types:**
+ - Path conflict risk because another webhook uses the same path with POST; this is valid only if methods differ as intended
+ - If workflow is inactive, public URL will not serve production webhook
+ - If response chain breaks before `Respond to Webhook`, callers may receive timeout/error
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Get Calendar Events
+
+- **Type and technical role:** `n8n-nodes-base.googleCalendar`
+ Retrieves existing events from the selected Google Calendar so occupied slots can be excluded.
+- **Configuration choices:**
+ - Operation: `getAll`
+ - Calendar: `primary`
+ - Return all results: enabled
+ - Time window:
+ - `timeMin = {{$now.toISO()}}`
+ - `timeMax = {{$now.plus({ days: 30 }).toISO()}}`
+- **Key expressions or variables used:**
+ - `$now.toISO()`
+ - `$now.plus({ days: 30 }).toISO()`
+- **Input and output connections:**
+ - Input ← `Webhook - Show Form`
+ - Output → `Calculate Available Slots`
+- **Version-specific requirements:** `typeVersion: 1.2`
+- **Edge cases or potential failure types:**
+ - Google OAuth2 credential errors
+ - Google API quota/rate limit issues
+ - Empty result set is acceptable and handled downstream
+ - All-day events and long events are passed through, but later filtered in code
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Calculate Available Slots
+
+- **Type and technical role:** `n8n-nodes-base.code`
+ Generates 30-minute free slots for the next 30 days by comparing configured working hours to fetched Google Calendar events.
+- **Configuration choices:**
+ - JavaScript code node
+ - Business rules:
+ - Working days: Monday to Friday (`[1,2,3,4,5]`)
+ - Working hours: `09:00` to `14:00`
+ - Slot duration: `30` minutes
+ - Planning horizon: `30` days
+ - Timezone strategy:
+ - Explicitly constructs slot timestamps with `+02:00`
+ - Comments indicate intended target timezone is Greece
+- **Key expressions or variables used:**
+ - `$input.all()`
+ - `event.start.dateTime`, `event.end.dateTime`
+ - Generated slot object:
+ - `value`: ISO timestamp with timezone offset
+ - `label`: human-readable display text
+- **Input and output connections:**
+ - Input ← `Get Calendar Events`
+ - Output → `Build HTML Form`
+- **Version-specific requirements:** `typeVersion: 2`
+- **Edge cases or potential failure types:**
+ - Timezone handling may be inaccurate during DST changes because the code hardcodes `+02:00`
+ - Comment says Greece is 1 hour ahead of Berlin, but DST can invalidate that assumption seasonally
+ - Events longer than 24 hours are intentionally ignored
+ - All-day events are ignored because only events with `start.dateTime` are considered
+ - If no free slots are found, returns one item with `{ noSlots: true, slots: [] }`; downstream HTML still renders but expects a compatible structure
+ - If calendar returns many overlapping events, computation remains simple but could become heavier with large result sets
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Build HTML Form
+
+- **Type and technical role:** `n8n-nodes-base.code`
+ Builds the entire front-end booking page as raw HTML, CSS, and JavaScript.
+- **Configuration choices:**
+ - Reads all slot items from input
+ - Groups slots by date
+ - Formats time labels manually to avoid browser timezone conversion
+ - Produces a complete HTML document with:
+ - Date navigator
+ - Slot buttons
+ - Hidden input for selected slot
+ - First name, last name, email, and message fields
+ - Client-side validation
+- **Key expressions or variables used:**
+ - `$input.all()`
+ - `slot.value`
+ - Embedded `slotsJson` in browser-side script
+ - Form field names:
+ - `timeslot`
+ - `firstName`
+ - `lastName`
+ - `email`
+ - `message`
+- **Input and output connections:**
+ - Input ← `Calculate Available Slots`
+ - Output → `Respond to Webhook`
+- **Version-specific requirements:** `typeVersion: 2`
+- **Edge cases or potential failure types:**
+ - If upstream returns `{ noSlots: true, slots: [] }` instead of normal slot items, the code still treats it as slot input; this can create malformed grouping because `slot.value` is expected
+ - Large embedded HTML is harder to maintain directly inside a Code node
+ - Browser-side JavaScript assumes `slotsByDate.length` exists and is valid
+ - Form action is empty (`action=""`), so the form posts back to the same URL; this is intended
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Respond to Webhook
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`
+ Sends the generated HTML booking page back to the requester.
+- **Configuration choices:**
+ - Response type: text
+ - Body: `{{$json.html}}`
+ - Response header: `Content-Type: text/html`
+- **Key expressions or variables used:**
+ - `$json.html`
+- **Input and output connections:**
+ - Input ← `Build HTML Form`
+ - No output
+- **Version-specific requirements:** `typeVersion: 1.1`
+- **Edge cases or potential failure types:**
+ - If `html` is missing from input, response body will be empty or invalid
+ - Wrong content type would break rendering, but header is correctly set
+- **Sub-workflow reference:** none
+
+---
+
+## 2.2 Booking Submission Processing
+
+### Overview
+
+This block handles the **POST** request generated when a user submits the form. It validates and transforms the chosen slot into a start/end time pair, creates the appointment in Google Calendar, builds formatted email data, sends a confirmation email, and returns an HTML confirmation page.
+
+### Nodes Involved
+
+- Webhook - Submit Form
+- Code in JavaScript
+- Create Calendar Event
+- Format Email Data
+- Send Confirmation Email
+- Show Success Message
+
+---
+
+### Node: Webhook - Submit Form
+
+- **Type and technical role:** `n8n-nodes-base.webhook`
+ Receives form submissions from the HTML page.
+- **Configuration choices:**
+ - Path: `booking`
+ - HTTP method: `POST`
+ - Response mode: `responseNode`
+- **Key expressions or variables used:** none
+- **Input and output connections:**
+ - No input
+ - Output → `Code in JavaScript`
+- **Version-specific requirements:** `typeVersion: 1.1`
+- **Edge cases or potential failure types:**
+ - Missing expected POST form fields
+ - If form encoding changes, `body` structure may differ
+ - If no response node completes, request may hang or timeout
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Code in JavaScript
+
+- **Type and technical role:** `n8n-nodes-base.code`
+ Parses the submitted timeslot and computes a 30-minute end time while preserving the original timezone offset.
+- **Configuration choices:**
+ - Reads `body` from the webhook payload
+ - Expects `timeslot` in format like `2025-11-21T10:30:00+02:00`
+ - Uses regex to extract date/time/offset parts
+ - Adds 30 minutes manually
+ - Returns a new `body` object including:
+ - `startTime`
+ - `endTime`
+- **Key expressions or variables used:**
+ - `$input.first().json.body`
+ - `webhookData.timeslot`
+ - Regex: `^(.+T)(\d{2}):(\d{2}):(\d{2})(\+\d{2}:\d{2})$`
+- **Input and output connections:**
+ - Input ← `Webhook - Submit Form`
+ - Output → `Create Calendar Event`
+- **Version-specific requirements:** `typeVersion: 2`
+- **Edge cases or potential failure types:**
+ - Throws `Invalid time format` if the slot does not match the expected pattern
+ - Regex only supports positive timezone offsets like `+02:00`; it does not support `Z` or negative offsets
+ - Manual addition of 30 minutes can produce hour values beyond 23 and does not normalize date rollover properly
+ - Assumes all bookings are exactly 30 minutes
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Create Calendar Event
+
+- **Type and technical role:** `n8n-nodes-base.googleCalendar`
+ Creates the booked appointment in Google Calendar.
+- **Configuration choices:**
+ - Calendar: `primary`
+ - Start: `{{$json.body.timeslot}}`
+ - End: `{{$json.body.endTime}}`
+ - Summary: `Meeting with {{ $json.body.firstName }} {{ $json.body.lastName }}`
+ - Description includes:
+ - customer full name
+ - email
+ - message fallback to `No message provided`
+- **Key expressions or variables used:**
+ - `$json.body.timeslot`
+ - `$json.body.endTime`
+ - `$json.body.firstName`
+ - `$json.body.lastName`
+ - `$json.body.email`
+ - `$json.body.message || 'No message provided'`
+- **Input and output connections:**
+ - Input ← `Code in JavaScript`
+ - Output → `Format Email Data`
+- **Version-specific requirements:** `typeVersion: 1.2`
+- **Edge cases or potential failure types:**
+ - Google OAuth2 authentication failure
+ - Calendar API quota/rate limit failure
+ - Event creation conflict is not pre-checked at write time; race conditions are possible if two users submit the same slot near-simultaneously
+ - Invalid start/end timestamps cause API rejection
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Format Email Data
+
+- **Type and technical role:** `n8n-nodes-base.code`
+ Combines Google Calendar event data with original form fields and creates user-friendly date/time strings for email content.
+- **Configuration choices:**
+ - Reads event details from current input
+ - Reads original form payload from `Webhook - Submit Form`
+ - Formats:
+ - `formattedDate`
+ - `formattedTime`
+ - Adds:
+ - `firstName`
+ - `lastName`
+ - `email`
+ - `message`
+- **Key expressions or variables used:**
+ - `$input.first().json`
+ - `$('Webhook - Submit Form').first().json.body`
+ - `eventData.start.dateTime`
+ - `eventData.end.dateTime`
+ - `toLocaleDateString('en-US', ...)`
+ - `toLocaleTimeString('en-US', ...)`
+- **Input and output connections:**
+ - Input ← `Create Calendar Event`
+ - Output → `Send Confirmation Email`
+- **Version-specific requirements:** `typeVersion: 2`
+- **Edge cases or potential failure types:**
+ - Cross-node reference to `Webhook - Submit Form` assumes that node is present and reachable in execution data
+ - Formatting depends on server/runtime locale support
+ - Timezone display may reflect runtime interpretation of returned ISO timestamps
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Send Confirmation Email
+
+- **Type and technical role:** `n8n-nodes-base.gmail`
+ Sends a styled HTML confirmation email to the user.
+- **Configuration choices:**
+ - Recipient: `{{$json.email}}`
+ - Subject: `✅ Appointment Confirmed - {{ $json.formattedDate }}`
+ - HTML body includes:
+ - customer name
+ - date and time
+ - fixed duration of 30 minutes
+ - email address
+ - submitted message
+ - Google Calendar link via `htmlLink`
+- **Key expressions or variables used:**
+ - `$json.email`
+ - `$json.firstName`
+ - `$json.lastName`
+ - `$json.formattedDate`
+ - `$json.formattedTime`
+ - `$json.message`
+ - `$json.htmlLink`
+- **Input and output connections:**
+ - Input ← `Format Email Data`
+ - Output → `Show Success Message`
+- **Version-specific requirements:** `typeVersion: 2.1`
+- **Edge cases or potential failure types:**
+ - Gmail OAuth2 authentication issues
+ - Sending limits or Gmail API restrictions
+ - HTML link may not be ideal as an “Add to Google Calendar” CTA depending on what `htmlLink` returned by Google Calendar represents
+ - If recipient email is invalid, sending may fail
+- **Sub-workflow reference:** none
+
+---
+
+### Node: Show Success Message
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`
+ Returns a final branded success page to the browser after successful booking and email sending.
+- **Configuration choices:**
+ - Response type: text
+ - Static HTML success page
+ - Header: `Content-Type: text/html`
+- **Key expressions or variables used:** none
+- **Input and output connections:**
+ - Input ← `Send Confirmation Email`
+ - No output
+- **Version-specific requirements:** `typeVersion: 1.1`
+- **Edge cases or potential failure types:**
+ - If upstream email sending fails, this node is never reached
+ - User receives no success page if booking is created but email step errors
+- **Sub-workflow reference:** none
+
+---
+
+## 2.3 Shared Design / Documentation Layer
+
+### Overview
+
+This non-executing block contains a sticky note that explains the workflow structure and setup steps. It is useful operational documentation embedded directly in the canvas.
+
+### Nodes Involved
+
+- Sticky Note
+
+---
+
+### Node: Sticky Note
+
+- **Type and technical role:** `n8n-nodes-base.stickyNote`
+ Visual documentation node for human operators.
+- **Configuration choices:**
+ - Contains workflow description, path overview, and quick-start steps
+- **Key expressions or variables used:** none
+- **Input and output connections:** none
+- **Version-specific requirements:** `typeVersion: 1`
+- **Edge cases or potential failure types:** none in execution; only maintainability concerns if note becomes outdated
+- **Sub-workflow reference:** none
+
+---
+
+# 3. Summary Table
+
+| Node Name | Node Type | Functional Role | Input Node(s) | Output Node(s) | Sticky Note |
+|---|---|---|---|---|---|
+| Webhook - Show Form | Webhook | Public entry point for serving the booking page | | Get Calendar Events | ## Self-Hosted Booking Form with Google Calendar
This workflow has **two paths**:
**Top row (GET):** Serves the booking form
Webhook → Get Calendar Events → Calculate Available Slots → Build HTML Form → Respond
**Bottom row (POST):** Processes a booking submission
Webhook → Calculate End Time → Create Calendar Event → Format Email Data → Send Confirmation → Show Success Page
### Quick Start
1. Connect Google Calendar OAuth2
2. Connect Gmail OAuth2
3. Adjust availability settings in "Calculate Available Slots"
4. Activate & visit: `/webhook/booking` |
+| Get Calendar Events | Google Calendar | Retrieves upcoming occupied events in the next 30 days | Webhook - Show Form | Calculate Available Slots | ## Self-Hosted Booking Form with Google Calendar
This workflow has **two paths**:
**Top row (GET):** Serves the booking form
Webhook → Get Calendar Events → Calculate Available Slots → Build HTML Form → Respond
**Bottom row (POST):** Processes a booking submission
Webhook → Calculate End Time → Create Calendar Event → Format Email Data → Send Confirmation → Show Success Page
### Quick Start
1. Connect Google Calendar OAuth2
2. Connect Gmail OAuth2
3. Adjust availability settings in "Calculate Available Slots"
4. Activate & visit: `/webhook/booking` |
+| Calculate Available Slots | Code | Computes available 30-minute appointment slots | Get Calendar Events | Build HTML Form | ## Self-Hosted Booking Form with Google Calendar
This workflow has **two paths**:
**Top row (GET):** Serves the booking form
Webhook → Get Calendar Events → Calculate Available Slots → Build HTML Form → Respond
**Bottom row (POST):** Processes a booking submission
Webhook → Calculate End Time → Create Calendar Event → Format Email Data → Send Confirmation → Show Success Page
### Quick Start
1. Connect Google Calendar OAuth2
2. Connect Gmail OAuth2
3. Adjust availability settings in "Calculate Available Slots"
4. Activate & visit: `/webhook/booking` |
+| Build HTML Form | Code | Generates the booking page HTML/CSS/JS | Calculate Available Slots | Respond to Webhook | ## Self-Hosted Booking Form with Google Calendar
This workflow has **two paths**:
**Top row (GET):** Serves the booking form
Webhook → Get Calendar Events → Calculate Available Slots → Build HTML Form → Respond
**Bottom row (POST):** Processes a booking submission
Webhook → Calculate End Time → Create Calendar Event → Format Email Data → Send Confirmation → Show Success Page
### Quick Start
1. Connect Google Calendar OAuth2
2. Connect Gmail OAuth2
3. Adjust availability settings in "Calculate Available Slots"
4. Activate & visit: `/webhook/booking` |
+| Respond to Webhook | Respond to Webhook | Returns the generated booking page to the browser | Build HTML Form | | |
+| Webhook - Submit Form | Webhook | Public entry point for booking form submissions via POST | | Code in JavaScript | |
+| Code in JavaScript | Code | Parses selected slot and computes end time | Webhook - Submit Form | Create Calendar Event | |
+| Create Calendar Event | Google Calendar | Creates the booked event in Google Calendar | Code in JavaScript | Format Email Data | |
+| Format Email Data | Code | Combines calendar event output with original form data for email rendering | Create Calendar Event | Send Confirmation Email | |
+| Send Confirmation Email | Gmail | Sends HTML confirmation email to the customer | Format Email Data | Show Success Message | |
+| Show Success Message | Respond to Webhook | Returns a success HTML page after booking completion | Send Confirmation Email | | |
+| Sticky Note | Sticky Note | Embedded operational documentation | | | ## Self-Hosted Booking Form with Google Calendar
This workflow has **two paths**:
**Top row (GET):** Serves the booking form
Webhook → Get Calendar Events → Calculate Available Slots → Build HTML Form → Respond
**Bottom row (POST):** Processes a booking submission
Webhook → Calculate End Time → Create Calendar Event → Format Email Data → Send Confirmation → Show Success Page
### Quick Start
1. Connect Google Calendar OAuth2
2. Connect Gmail OAuth2
3. Adjust availability settings in "Calculate Available Slots"
4. Activate & visit: `/webhook/booking` |
+
+---
+
+# 4. Reproducing the Workflow from Scratch
+
+1. **Create a new workflow** in n8n.
+2. **Add a Sticky Note** and paste the following operational summary:
+ - Self-hosted booking form with Google Calendar
+ - Top row for GET form rendering
+ - Bottom row for POST submission processing
+ - Connect Google Calendar OAuth2 and Gmail OAuth2
+ - Visit `/webhook/booking` after activation
+
+## Build the GET path
+
+3. **Add a Webhook node** named **Webhook - Show Form**.
+ - Path: `booking`
+ - Response Mode: `Using Respond to Webhook Node`
+ - Leave it as the form-serving endpoint for browser access
+
+4. **Add a Google Calendar node** named **Get Calendar Events**.
+ - Credential: Google Calendar OAuth2
+ - Operation: **Get Many / Get All events**
+ - Calendar: `primary`
+ - Return All: enabled
+ - Set time window:
+ - `Time Min`: `{{$now.toISO()}}`
+ - `Time Max`: `{{$now.plus({ days: 30 }).toISO()}}`
+ - Connect **Webhook - Show Form → Get Calendar Events**
+
+5. **Add a Code node** named **Calculate Available Slots**.
+ - Paste logic that:
+ - reads all calendar events
+ - filters out all-day and >24h events
+ - scans the next 30 days
+ - keeps only Monday–Friday
+ - generates 30-minute slots from 09:00 to 14:00
+ - marks overlapping slots unavailable
+ - emits slot items with:
+ - `value`: ISO datetime with timezone offset
+ - `label`: display text
+ - Current workflow assumptions:
+ - working days `[1,2,3,4,5]`
+ - `startHour = 9`
+ - `endHour = 14`
+ - `slotDuration = 30`
+ - timezone offset hardcoded to `+02:00`
+ - Connect **Get Calendar Events → Calculate Available Slots**
+
+6. **Add a Code node** named **Build HTML Form**.
+ - Paste code that:
+ - reads all slot items from input
+ - groups them by date
+ - generates full HTML page
+ - includes embedded CSS and client-side JavaScript
+ - includes a `