AutomationFlowsAI & RAG › AI Appointment Scheduling Assistant

AI Appointment Scheduling Assistant

AI Appointment Scheduling Assistant. Uses openAi. Webhook trigger; 7 nodes.

Webhook trigger★★★★☆ complexityAI-powered7 nodesOpenAI
AI & RAG Trigger: Webhook Nodes: 7 Complexity: ★★★★☆ AI nodes: yes Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "AI Appointment Scheduling Assistant",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "appointment-request",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "appt-node-001",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        360
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "appt-field-001",
              "name": "client_name",
              "value": "={{ $json.body.client_name || 'Unknown' }}",
              "type": "string"
            },
            {
              "id": "appt-field-002",
              "name": "client_email",
              "value": "={{ $json.body.client_email || '' }}",
              "type": "string"
            },
            {
              "id": "appt-field-003",
              "name": "requested_date",
              "value": "={{ $json.body.requested_date || '' }}",
              "type": "string"
            },
            {
              "id": "appt-field-004",
              "name": "requested_time",
              "value": "={{ $json.body.requested_time || '' }}",
              "type": "string"
            },
            {
              "id": "appt-field-005",
              "name": "appointment_type",
              "value": "={{ $json.body.appointment_type || 'general' }}",
              "type": "string"
            },
            {
              "id": "appt-field-006",
              "name": "reason",
              "value": "={{ $json.body.reason || '' }}",
              "type": "string"
            },
            {
              "id": "appt-field-007",
              "name": "preferred_channel",
              "value": "={{ $json.body.preferred_channel || 'video' }}",
              "type": "string"
            },
            {
              "id": "appt-field-008",
              "name": "received_at",
              "value": "={{ $now.toISO() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "appt-node-002",
      "name": "Normalize Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        460,
        360
      ]
    },
    {
      "parameters": {
        "resource": "text",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are an intelligent appointment scheduling assistant. Analyze the incoming appointment request and return ONLY a valid JSON object. No markdown. No explanation. No extra text.\n\nToday's context: appointments are scheduled during business hours (Monday\u2013Friday, 09:00\u201317:00). Same-day or next-day requests are considered urgent.\n\nReturn exactly this JSON structure:\n{\n  \"urgency\": \"one of: routine | urgent | emergency\",\n  \"validated_date\": \"the requested date in YYYY-MM-DD format, or null if unparseable\",\n  \"validated_time\": \"the requested time in HH:MM 24h format, or null if unparseable\",\n  \"is_business_hours\": true or false,\n  \"suggested_slot\": \"a concrete alternative datetime string if the request is outside business hours or unclear, otherwise repeat the requested slot\",\n  \"appointment_duration_minutes\": integer \u2014 estimated meeting duration based on appointment type and reason,\n  \"classification\": \"one of: consultation | follow_up | demo | support | onboarding | general\",\n  \"channel_recommendation\": \"one of: video | phone | in_person\",\n  \"preparation_notes\": \"1-2 sentences the host should read before the meeting\",\n  \"client_confirmation_message\": \"a warm, professional 2-3 sentence message to send to the client confirming the booking intent\"\n}\n\nUrgency rules:\n- emergency: reason contains words like urgent, critical, emergency, ASAP, immediately\n- urgent: requested_date is today or tomorrow\n- routine: everything else"
            },
            {
              "role": "user",
              "content": "Process this appointment request:\n\nClient Name: {{ $json.client_name }}\nClient Email: {{ $json.client_email }}\nRequested Date: {{ $json.requested_date }}\nRequested Time: {{ $json.requested_time }}\nAppointment Type: {{ $json.appointment_type }}\nReason: {{ $json.reason }}\nPreferred Channel: {{ $json.preferred_channel }}\nRequest Received: {{ $json.received_at }}"
            }
          ]
        },
        "simplify": false,
        "options": {
          "temperature": 0.2,
          "maxTokens": 600
        }
      },
      "id": "appt-node-003",
      "name": "Analyze Request with OpenAI",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        680,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first();\n\n// Step 1: Extract raw text from OpenAI response\nlet rawText = '';\ntry {\n  rawText = item.json.choices[0].message.content;\n} catch (e) {\n  rawText = '';\n}\n\n// Step 2: Strip markdown fences and parse JSON\nlet parsed;\ntry {\n  const cleaned = rawText\n    .replace(/```json/gi, '')\n    .replace(/```/g, '')\n    .trim();\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  parsed = {};\n}\n\n// Step 3: Allowed value sets\nconst validUrgency = ['routine', 'urgent', 'emergency'];\nconst validClassification = ['consultation', 'follow_up', 'demo', 'support', 'onboarding', 'general'];\nconst validChannel = ['video', 'phone', 'in_person'];\n\n// Step 4: Validate and apply safe defaults for every field\nconst result = {\n  urgency: validUrgency.includes(parsed.urgency)\n    ? parsed.urgency\n    : 'routine',\n\n  validated_date: (typeof parsed.validated_date === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(parsed.validated_date))\n    ? parsed.validated_date\n    : null,\n\n  validated_time: (typeof parsed.validated_time === 'string' && /^\\d{2}:\\d{2}$/.test(parsed.validated_time))\n    ? parsed.validated_time\n    : null,\n\n  is_business_hours: typeof parsed.is_business_hours === 'boolean'\n    ? parsed.is_business_hours\n    : true,\n\n  suggested_slot: (typeof parsed.suggested_slot === 'string' && parsed.suggested_slot.length > 0)\n    ? parsed.suggested_slot\n    : 'Next available weekday at 10:00',\n\n  appointment_duration_minutes: (Number.isInteger(parsed.appointment_duration_minutes) && parsed.appointment_duration_minutes > 0)\n    ? parsed.appointment_duration_minutes\n    : 30,\n\n  classification: validClassification.includes(parsed.classification)\n    ? parsed.classification\n    : 'general',\n\n  channel_recommendation: validChannel.includes(parsed.channel_recommendation)\n    ? parsed.channel_recommendation\n    : 'video',\n\n  preparation_notes: (typeof parsed.preparation_notes === 'string' && parsed.preparation_notes.length > 0)\n    ? parsed.preparation_notes\n    : 'Review client history before the meeting.',\n\n  client_confirmation_message: (typeof parsed.client_confirmation_message === 'string' && parsed.client_confirmation_message.length > 0)\n    ? parsed.client_confirmation_message\n    : 'Thank you for your request. We will confirm your appointment shortly.'\n};\n\nreturn [{ json: result }];"
      },
      "id": "appt-node-004",
      "name": "Validate & Structure Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        360
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "urgency-check",
              "leftValue": "={{ $json.urgency }}",
              "rightValue": "routine",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "appt-node-005",
      "name": "Urgent or Emergency?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1120,
        360
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, priority_flag: true, appointment: { urgency: $json.urgency, classification: $json.classification, validated_date: $json.validated_date, validated_time: $json.validated_time, is_business_hours: $json.is_business_hours, suggested_slot: $json.suggested_slot, appointment_duration_minutes: $json.appointment_duration_minutes, channel_recommendation: $json.channel_recommendation, preparation_notes: $json.preparation_notes, client_confirmation_message: $json.client_confirmation_message } }) }}",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "X-Appointment-Priority",
                "value": "high"
              }
            ]
          }
        }
      },
      "id": "appt-node-006",
      "name": "Respond \u2014 Priority Booking",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1340,
        220
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, priority_flag: false, appointment: { urgency: $json.urgency, classification: $json.classification, validated_date: $json.validated_date, validated_time: $json.validated_time, is_business_hours: $json.is_business_hours, suggested_slot: $json.suggested_slot, appointment_duration_minutes: $json.appointment_duration_minutes, channel_recommendation: $json.channel_recommendation, preparation_notes: $json.preparation_notes, client_confirmation_message: $json.client_confirmation_message } }) }}",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "X-Appointment-Priority",
                "value": "normal"
              }
            ]
          }
        }
      },
      "id": "appt-node-007",
      "name": "Respond \u2014 Routine Booking",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1340,
        500
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Input": {
      "main": [
        [
          {
            "node": "Analyze Request with OpenAI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Request with OpenAI": {
      "main": [
        [
          {
            "node": "Validate & Structure Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate & Structure Response": {
      "main": [
        [
          {
            "node": "Urgent or Emergency?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Urgent or Emergency?": {
      "main": [
        [
          {
            "node": "Respond \u2014 Priority Booking",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond \u2014 Routine Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "versionId": "1.0.0",
  "tags": [
    {
      "name": "appointment-scheduling"
    },
    {
      "name": "ai-automation"
    },
    {
      "name": "openai"
    },
    {
      "name": "calendar"
    }
  ]
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AI Appointment Scheduling Assistant. Uses openAi. Webhook trigger; 7 nodes.

Source: https://github.com/nextwave-ai/ai-automation-portfolio/blob/main/Projects/AI-Appointment-Scheduling-Assistant/workflow.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

The Ultimate Scraper for n8n uses Selenium and AI to retrieve any information displayed on a webpage. You can also use session cookies to log in to the targeted webpage for more advanced scraping need

OpenAI Chat, HTTP Request, Information Extractor +1
AI & RAG

z-Api. Uses httpRequest, openAi, redis, postgres. Webhook trigger; 61 nodes.

HTTP Request, OpenAI, Redis +4
AI & RAG

This demo workflow receives an SMS-style request through a webhook, routes common farmer commands while it uses OpenAI for unknown messages. It is designed for African SMS-first farming contexts but c

OpenAI
AI & RAG

How it works: • Receives WhatsApp messages via webhook from Whapi.Cloud • Routes commands: AI chat (/ai), numeric commands (1-9), or help menu • Sends responses: text, images, documents, videos, conta

HTTP Request, OpenAI
AI & RAG

This workflow will allow you to use OpenAI Assistant API together with a chatting platform. This version is configured to work with Hubspot, however, the Hubspot modules can be replaced by other platf

HTTP Request, Airtable, OpenAI