Nodes:
name: n8n Form Trigger
type: n8n-nodes-base.formTrigger
parameters: path: schedule_appointment
options: ignoreBots: yes
appendAttribution: yes
useWorkflowTimezone: yes
formTitle: Schedule an Appointment
formFields: values: fieldLabel: Your Name
placeholder: eg. Sam Smith
requiredField: yes
fieldType: email
fieldLabel: Email
placeholder: eg. sam@example.com
requiredField: yes
fieldType: textarea
fieldLabel: Enquiry
placeholder: eg. I'm looking for...
requiredField: yes
formDescription: Welcome to Jim's Appointment Form.
Before we set a date, please tell me a little about yourself and how I can help.
name: Form End
type: n8n-nodes-base.form
parameters: options: formTitle: Appointment Request Sent!
operation: completion
completionTitle: Appointment Request Sent!
completionMessage: =Thank you for submitting an appointment request. A confirmation of this request will be sent to your inbox. I'll get back to you shortly with a confirmation of the appointment.

Here is the summary of the appointment request.

Name: {{ $('Get Form Values').item.json.name }}
Date & Time: {{ DateTime.fromISO($('Get Form Values').item.json.dateTime).format('EEE, dd MMM @ t') }}
Enquiry: {{ $('Get Form Values').item.json.enquiry.trim() }}

name: Enter Date & Time
type: n8n-nodes-base.form
parameters: options: formTitle: Enter a Date & Time
formDescription: =Please select a date and time
defineForm: json
jsonOutput: ={{
[
   {
      "fieldLabel":"Date",
      "requiredField":true,
      "fieldType": "dropdown",
      "fieldOptions":
        Array(5).fill(0)
          .map((_,idx) => $now.plus(idx+1, 'day'))
          .filter(d => !d.isWeekend)
          .map(d => ({ option: d.format('EEE, d MMM') }))
   },
   {
     "fieldLabel": "Time",
     "requiredField": true,
     "fieldType": "dropdown",
     "fieldOptions": [
        { "option": "9:00 am" },
        { "option": "10:00 am" },
        { "option": "11:00 am" },
        { "option": "12:00 pm" },
        { "option": "1:00 pm" },
        { "option": "2:00 pm" },
        { "option": "3:00 pm" },
        { "option": "4:00 pm" },
        { "option": "5:00 pm" },
        { "option": "6:00 pm" }
     ]
   }
]
}}
name: Get Form Values
type: n8n-nodes-base.set
parameters: mode: raw
jsonOutput: ={{
{
  name: $('n8n Form Trigger').first().json['Your Name'],
  email: $('n8n Form Trigger').first().json.Email,
  enquiry: $('n8n Form Trigger').first().json.Enquiry,
  dateTime: DateTime.fromFormat(`${$json.Date} ${$json.Time}`, "EEE, dd MMM t"),
  submittedAt: $('n8n Form Trigger').first().json.submittedAt,
}
}}
name: Terms & Conditions
type: n8n-nodes-base.form
parameters: options: formTitle: Before we continue...
formDescription: =Terms and Conditions for Booking an Appointment

Non-Binding Nature of Discussions:
Any information shared, discussed, or agreed upon during the call is non-binding and provisional. No agreement, service, or commitment shall be considered confirmed unless explicitly documented and agreed to in writing.

Prohibition of Recording and Note-Taking Tools:
By proceeding with the appointment, the user agrees not to use AI assistants, note-taking applications, recording devices, or any other technology to record or transcribe the conversation, whether manually or automatically. This is to ensure confidentiality and respect for the integrity of the discussion.

Confirmation of Understanding:
By booking this appointment, you acknowledge and accept these terms and conditions in full.
formFields: values: fieldType: dropdown
fieldLabel: Please select
multiselect: yes
fieldOptions: values: I accept the terms and conditions
requiredField: yes
name: OpenAI Chat Model
type: @n8n/n8n-nodes-langchain.lmChatOpenAi
name: Has Accepted?
type: n8n-nodes-base.if
parameters: conditions: options: version: 2
caseSensitive: yes
typeValidation: strict
combinator: and
conditions: operator: type: string
operation: startsWith
leftValue: ={{ $json["Please select"] }}
rightValue: I accept
name: Send Receipt
type: n8n-nodes-base.gmail
parameters: sendTo: ={{ $('Get Form Values').first().json.email }}
message: =<p>Dear {{ $('Get Form Values').first().json.name }},</p>
<p>Thanks for requesting an appointment. We will review and get back to you shortly.</p>
<p>Here is the summary of the request that was sent:</p>
<p>
Name: {{ $('Get Form Values').first().json.name }}<br/>
Email: {{ $('Get Form Values').first().json.email }}<br/>
Enquiry: {{ $('Get Form Values').first().json.enquiry }}<br/>
Submitted at: {{ $('Get Form Values').first().json.submittedAt }}
</p>

subject: =Appointment Request Received for {{ DateTime.fromISO($('Get Form Values').first().json.dateTime).format('EEE, dd MMM @ t') }}
name: Wait for Approval
type: n8n-nodes-base.gmail
parameters: sendTo: =admin@example.com
message: =<h2>A new appointment request was submitted!</h2>
<p>
Requesting appointment date is <strong>{{ DateTime.fromISO($('Execute Workflow Trigger').item.json.dateTime).format('EEE, dd MMM @ t') }}</strong>.
</p>
<p>
Name: {{ $('Execute Workflow Trigger').first().json.name }}<br/>
Email: {{ $('Execute Workflow Trigger').first().json.email }}<br/>
Enquiry Summary: {{ $json.text }}<br/>
Submitted at: {{ $('Execute Workflow Trigger').first().json.submittedAt }}
</p>
subject: New Appointment Request!
operation: sendAndWait
approvalOptions: values: approvalType: double
approveLabel: Confirm
name: Has Approval?
type: n8n-nodes-base.if
parameters: conditions: options: version: 2
caseSensitive: yes
typeValidation: strict
combinator: and
conditions: operator: type: boolean
operation: true
singleValue: yes
leftValue: ={{ $json.data.approved }}
name: OpenAI Chat Model1
type: @n8n/n8n-nodes-langchain.lmChatOpenAi
name: Create Appointment
type: n8n-nodes-base.googleCalendar
parameters: end: ={{ DateTime.fromISO($('Execute Workflow Trigger').first().json.dateTime).plus(30, 'minute').toISO() }}
start: ={{ $('Execute Workflow Trigger').first().json.dateTime }}
calendar: __rl: yes
mode: list
value: c_5792bdf04bc395cbcbc6f7b754268245a33779d36640cc80a357711aa2f09a0a@group.calendar.google.com
cachedResultName: n8n-events
additionalFields: summary: =Appointment Scheduled - {{ $('Execute Workflow Trigger').item.json.name }} & Jim
attendees: ={{ $('Execute Workflow Trigger').item.json.email }}
description: ={{ $('Summarise Enquiry').first().json.text }}

Original message:
> {{ $('Execute Workflow Trigger').item.json.enquiry }}
conferenceDataUi: conferenceDataValues: conferenceSolution: hangoutsMeet
name: Send Rejection
type: n8n-nodes-base.gmail
parameters: sendTo: ={{ $('Execute Workflow Trigger').first().json.email }}
message: =<p>Dear {{ $('Execute Workflow Trigger').first().json.name }},</p>
<p>Unfortunately, we cannot schedule the requested appointment at the requested time.</p>
<p>Kind regards</p>

subject: =Appointment Request Rejected for {{ DateTime.fromISO($('Execute Workflow Trigger').first().json.dateTime).format('EEE, dd MMM @ t') }}
name: Sticky Note3
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 763.0427617951669
height: 611.898918296892
content: ## 1. Qualify Enquiries Using AI
[Learn more about the text classifier](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.text-classifier/)

With n8n's multi-forms, you’re no longer stuck creating long, overwhelming forms. Instead, you have more flexibility and control to design smarter, more engaging form experiences.

In this demo, we’ll explore an appointment request scenario where a user wants to schedule a call to discuss their inquiry. However, not all inquiries require a meeting, making it a perfect use case for AI to pre-qualify the request. We can handle this validation using the text classifier node.
name: Sticky Note6
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 781.472405063291
height: 606.0718987341766
content: ## 2. Split Form For Better User Experience
[Learn more about the forms](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.form)

Onboarding is a great reason to split your big form into smaller ones. Taking the user through a step by step process ensures a smooth experience and keeps them engaged throughout.

Here, we take the opportunity of the extra context space to display a terms and conditions which the user must agree to making their request. The next form then asks for desired date and time of the event.
name: Decline
type: n8n-nodes-base.form
parameters: operation: completion
completionTitle: Send me a DM Instead!
completionMessage: Thanks for your enquiry but it may not necessarily need an appointment. Please feel free to email me instead at jim@example.com.
name: Decline1
type: n8n-nodes-base.form
parameters: operation: completion
completionTitle: Send me a DM Instead!
completionMessage: Thanks for your enquiry but it may not necessarily need an appointment. Please feel free to email me instead at jim@example.com.
name: Sticky Note7
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 792.9401150747982
height: 497.4250863060987
content: ## 3. Send Acknowledgement to User and Start Approval Process
[Learn more about the Gmail node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/)

Once all form steps are concluded, we can send a notification to the requester via email and in the background, trigger another email to the admin to initiate the approval process. The approval process works in a separate execution so doesn't interrupt the user's form experience.
name: Sticky Note8
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 609.4228768699652
height: 287.178089758343
content: ## 4. Approve or Decline Appointment
[Learn more about the Waiting for Approval](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/message-operations/#send-a-message-and-wait-for-approval)

The Wait for Approval feature for Gmail is a special operation which allows for human-in-the-loop interaction in n8n workflows. In this example, the human interaction is the approval of the appointment request. The feature will put the workflow in a waiting state where a message is sent to the admin with 2 buttons: confirm and decline.

When the admin clicks on the confirm button, the workflow resumes from the Gmail node and a meeting event is created for the requesting user in Google Calendar.

When declined, a rejection email is sent to the requester instead.
name: Trigger Approval Process
type: n8n-nodes-base.executeWorkflow
parameters: mode: each
options: waitForSubWorkflow: no
workflowId: __rl: yes
mode: id
value: ={{ $workflow.id }}
name: Execute Workflow Trigger
type: n8n-nodes-base.executeWorkflowTrigger
name: Summarise Enquiry
type: @n8n/n8n-nodes-langchain.chainLlm
parameters: text: =The enquiry is as follows:
{{ $('Execute Workflow Trigger').first().json.enquiry.substring(0, 500) }}
messages: messageValues: Summarise the given enquiry
promptType: define
name: Sticky Note9
type: n8n-nodes-base.stickyNote
parameters: width: 468.6766398158801
height: 690.6653164556957
content: ## Try it out!

### This n8n template is a simple appointment scheduling workflow using n8n forms with AI thrown in the mix for good measure. It also uses n8n's wait for approval feature which allows the ability to confirm appointment requests and create events in Google Calendar.

### How it works
* We start with a form trigger which asks for the purpose of the appointment.
* Instantly, we can qualify this by using a text classifier node which uses AI's contextual understanding to ensure the appointment is worthwhile. If not, an alternative is suggested instead.
* Multi-page forms are then used to set the terms of the appointment and ask the user for a desired date and time.
* An acknowledgement is sent to the user while an approval by email process is triggered in the background.
* In a subworkflow, we use Gmail with the wait for approval operation to send an approval form to the admin user who can either confirm or decline the appointment request.
* When approved, a Google Calendar event is created. When declined, the user is notified  via email that the appointment request was declined.

### Need Help?
Join the [Discord](https://discord.com/invite/XPKeKXeB7d) or ask in the [Forum](https://community.n8n.io/)!

Happy Hacking!

name: Sticky Note
type: n8n-nodes-base.stickyNote
parameters: width: 177.66444188722656
height: 257.56869965477557
content: 














### 🚨 Set your admin email here!
name: Enquiry Classifier
type: @n8n/n8n-nodes-langchain.textClassifier
parameters: options: fallback: other
inputText: ={{ $json.Enquiry }}
categories: categories: category: relevant enquiry
description: Enquire about AI, automation, digital products and product engineering.

Connections:
Send Receipt: main: node: Form End
type: main
Has Accepted?: main: node: Enter Date & Time
type: main
node: Decline1
type: main
Has Approval?: main: node: Create Appointment
type: main
node: Send Rejection
type: main
Get Form Values: main: node: Trigger Approval Process
type: main
n8n Form Trigger: main: node: Enquiry Classifier
type: main
Enter Date & Time: main: node: Get Form Values
type: main
OpenAI Chat Model: ai_languageModel: node: Enquiry Classifier
type: ai_languageModel
Summarise Enquiry: main: node: Wait for Approval
type: main
Wait for Approval: main: node: Has Approval?
type: main
Enquiry Classifier: main: node: Terms & Conditions
type: main
node: Decline
type: main
OpenAI Chat Model1: ai_languageModel: node: Summarise Enquiry
type: ai_languageModel
Terms & Conditions: main: node: Has Accepted?
type: main
Execute Workflow Trigger: main: node: Summarise Enquiry
type: main
Trigger Approval Process: main: node: Send Receipt
type: main