{
  "name": "Lunexo – Appointment Booking Bot",
  "nodes": [
    {
      "parameters": {
        "content": "## Appointment Booking Bot\n\n**What it does:** Receives booking requests via webhook, checks slot availability, sends a confirmation or \"fully booked\" email, and responds accordingly.\n\n**Setup (~5 min):**\n1. Add your SMTP credentials\n2. Change `YOUR_ADMIN_EMAIL` in the Code node\n3. Edit the `availableSlots` array to match your schedule\n4. Activate the workflow\n5. Point your booking form to the webhook URL\n\n**Variables to change:**\n- `YOUR_ADMIN_EMAIL`\n- `availableSlots` array in Code node",
        "height": 300,
        "width": 350,
        "color": 5
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000001",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [40, 80]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "book-appointment",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000002",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [240, 300],
      "webhookId": "c2d3e4f5-a6b7-8901-bcde-200000000010"
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst body = input.body || input;\nconst name = body.name || body.fullName || 'Guest';\nconst email = body.email || '';\nconst date = body.date || '';\nconst time = body.time || '';\nconst service = body.service || 'General Consultation';\n\n// Define your available slots here\nconst availableSlots = [\n  { date: '2026-03-16', time: '09:00' },\n  { date: '2026-03-16', time: '10:00' },\n  { date: '2026-03-16', time: '11:00' },\n  { date: '2026-03-16', time: '14:00' },\n  { date: '2026-03-16', time: '15:00' },\n  { date: '2026-03-17', time: '09:00' },\n  { date: '2026-03-17', time: '10:00' },\n  { date: '2026-03-17', time: '14:00' },\n];\n\nconst isAvailable = availableSlots.some(s => s.date === date && s.time === time);\n\nconst confirmHtml = `<div style=\"font-family:Arial,sans-serif;max-width:600px;margin:0 auto;padding:20px;\">` + `<h2 style=\"color:#6C2BD9;\">Booking Confirmed!</h2>` + `<p>Hi ${name}, your appointment has been confirmed.</p>` + `<div style=\"background:#f4f0ff;border-left:4px solid #6C2BD9;padding:12px 16px;margin:16px 0;\">` + `<strong>Service:</strong> ${service}<br/>` + `<strong>Date:</strong> ${date}<br/>` + `<strong>Time:</strong> ${time}` + `</div>` + `<p>If you need to reschedule, please reply to this email.</p>` + `<p style=\"color:#888;font-size:12px;\">— Lunexo</p></div>`;\n\nconst unavailableHtml = `<div style=\"font-family:Arial,sans-serif;max-width:600px;margin:0 auto;padding:20px;\">` + `<h2 style=\"color:#6C2BD9;\">Slot Unavailable</h2>` + `<p>Hi ${name}, unfortunately the requested time slot (${date} at ${time}) is not available.</p>` + `<p>Please try one of our other available slots or contact us for assistance.</p>` + `<p style=\"color:#888;font-size:12px;\">— Lunexo</p></div>`;\n\nreturn [{ json: { name, email, date, time, service, isAvailable, confirmHtml, unavailableHtml } }];"
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000003",
      "name": "Extract Booking",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [460, 300]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c2d3e4f5-a6b7-8901-bcde-200000000020",
              "leftValue": "={{ $json.isAvailable }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000004",
      "name": "Check Availability",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "fromEmail": "noreply@yourdomain.com",
        "toEmail": "={{ $json.email }}",
        "subject": "=Booking Confirmed — {{ $json.service }} on {{ $json.date }}",
        "emailType": "html",
        "html": "={{ $json.confirmHtml }}",
        "options": {}
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000005",
      "name": "Send Confirmation",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [900, 200],
      "credentials": {
        "smtp": {
          "id": "CHANGE_ME",
          "name": "Your SMTP Account"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, status: 'confirmed', message: 'Appointment booked successfully.', date: $json.date, time: $json.time, service: $json.service }) }}",
        "options": {}
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000006",
      "name": "Respond Confirmed",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [1120, 200]
    },
    {
      "parameters": {
        "fromEmail": "noreply@yourdomain.com",
        "toEmail": "={{ $json.email }}",
        "subject": "=Slot Unavailable — {{ $json.date }} at {{ $json.time }}",
        "emailType": "html",
        "html": "={{ $json.unavailableHtml }}",
        "options": {}
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000007",
      "name": "Send Fully Booked",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [900, 420],
      "credentials": {
        "smtp": {
          "id": "CHANGE_ME",
          "name": "Your SMTP Account"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, status: 'unavailable', message: 'The requested time slot is not available. Please choose another slot.' }) }}",
        "options": {}
      },
      "id": "c2d3e4f5-a6b7-8901-bcde-200000000008",
      "name": "Respond Unavailable",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [1120, 420]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Extract Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Booking": {
      "main": [
        [
          {
            "node": "Check Availability",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Availability": {
      "main": [
        [
          {
            "node": "Send Confirmation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Fully Booked",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Confirmation": {
      "main": [
        [
          {
            "node": "Respond Confirmed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Fully Booked": {
      "main": [
        [
          {
            "node": "Respond Unavailable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "c2d3e4f5-a6b7-8901-bcde-200000000099",
  "meta": {
    "templateCredsSetupCompleted": false,
    "instanceId": "lunexo"
  },
  "pinData": {},
  "staticData": null,
  "tags": [
    { "name": "Lunexo" },
    { "name": "Scheduling" },
    { "name": "Booking" }
  ]
}
