{
  "id": "acSx6sonoNgqAacl",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Theater Ticket Booking Automation with AI Validation and Seat Assignment",
  "tags": [
    {
      "id": "uMLs7dA8SRRxqBDa",
      "name": "lost-and-found",
      "createdAt": "2026-07-01T08:17:37.751Z",
      "updatedAt": "2026-07-01T08:17:37.751Z"
    },
    {
      "id": "u64BWqiryEWCLPrS",
      "name": "ai-automation",
      "createdAt": "2026-07-01T08:17:37.757Z",
      "updatedAt": "2026-07-01T08:17:37.757Z"
    },
    {
      "id": "rxkRFqL8M1GqxTWW",
      "name": "notifications",
      "createdAt": "2026-07-01T08:17:37.823Z",
      "updatedAt": "2026-07-01T08:17:37.823Z"
    },
    {
      "id": "SSFhUWhVoOuEoY2W",
      "name": "real-time-tracking",
      "createdAt": "2026-07-01T08:17:37.823Z",
      "updatedAt": "2026-07-01T08:17:37.823Z"
    }
  ],
  "nodes": [
    {
      "id": "d96064a0-5529-4c34-bde0-7f5f867e040b",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2752,
        -800
      ],
      "parameters": {
        "width": 980,
        "height": 1362,
        "content": "## Theater Ticket Booking Automation\n\nThis workflow handles the complete theater ticketing lifecycle \u2014 from booking intake through AI validation, live seat assignment, pricing calculation, and confirmation delivery via email and SMS \u2014 with full tracking in Google Sheets.\n\n### Who's it for\n- Theater operators and box offices automating online reservations\n- Event managers who want AI-validated bookings before confirming seats\n- Small theater companies replacing manual seat-assignment spreadsheets\n- Venue teams handling high ticket volumes without extra staff\n\n### How it works\n1. Customer submits a booking request via webhook POST (show, date, time, seat category, ticket count, contact info)\n2. AI validates and normalizes the request, checks for unreasonable inputs, and scores it 1\u201310\n3. Requests scoring below 5 are filtered out immediately\n4. Workflow fetches the live seat map for that show and date from Google Sheets\n5. JS checks whether enough seats remain in the requested category and assigns specific seat numbers\n6. Requests with no available seats are filtered out with no charge\n7. JS calculates final pricing: base price per seat, 10% bulk discount for 4+ tickets, and 8% tax\n8. AI writes a warm personalized confirmation message plus a tasteful upsell suggestion\n9. Final booking record is assembled with a QR code payload string\n10. Booking is logged to Google Sheets, confirmation email and SMS are sent in parallel\n11. Webhook responds with the full booking confirmation JSON\n\n### How to set up\n1. Import this workflow into n8n\n2. Add Anthropic Claude credentials and attach to both Anthropic model nodes\n3. Add SendGrid credentials and replace YOUR_SENDGRID_API_KEY in the email node\n4. Add Twilio Basic Auth credentials and replace YOUR_TWILIO_ACCOUNT_SID and YOUR_TWILIO_PHONE_NUMBER\n5. Switch both Google Sheets HTTP nodes to the native Google Sheets node with OAuth2, or add OAuth2 credentials directly\n6. Replace YOUR_SHEET_ID in both Sheets nodes with your actual Spreadsheet ID\n7. Set up your Google Sheet with two tabs: SeatMap and BookingTracker (column layouts below)\n8. Activate and POST a test booking to the webhook URL\n\n### Requirements\n- Anthropic Claude API key (claude-sonnet-4 recommended)\n- SendGrid API key for email delivery\n- Twilio account for SMS delivery\n- Google Sheets with OAuth2 credentials (SeatMap + BookingTracker tabs)\n- n8n instance with a publicly accessible webhook URL\n\n### How to customize\n- Raise or lower the validation score threshold in Filter - Valid Bookings Only (default: score \u2265 5)\n- Edit base prices per seat category in JS - Calculate Ticket Pricing (default: VIP $150, Premium $90, Standard $55)\n- Adjust the bulk discount rate and tax rate in the same node\n- Add a real QR/barcode generation API call after JS - Format Final Booking Record\n- Add a condition before Send SMS Notification to skip SMS when customerPhone is empty\n- Chain a follow-up scheduled workflow to send 24hr pre-show reminders\n\n### Google Sheets Column Layout\n\nSeatMap tab \u2014 row 1 headers:\nShow Name | Show Date | Show Time | Seat Category | Available Seats | Next Seat Number Start\n\nBookingTracker tab \u2014 row 1 headers:\nExport Date | Booking ID | Show Name | Show Date | Show Time | Seat Category | Assigned Seats | Ticket Count | Total Price | Customer Name | Customer Email | Status | Confirmed At"
      },
      "typeVersion": 1
    },
    {
      "id": "cdd6ca99-fa50-4b02-bda4-ed954fe27e23",
      "name": "Sticky Note - Stage 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3872,
        -464
      ],
      "parameters": {
        "color": 4,
        "width": 1200,
        "height": 820,
        "content": "## Stage 1: Booking Intake & AI Validation\n\nAccepts booking requests via webhook POST. All fields have sensible defaults so the workflow can be tested without a payload.\n\nThe Set node normalizes all field name variants and generates a unique bookingId (TIX- prefix + timestamp).\n\nThe AI validation node (claude-sonnet-4, temperature 0.2 for accuracy) checks the show name, date format, time format, seat category, and ticket count for reasonableness. It returns a structured JSON with a validation score out of 10 and a list of upsell suggestions for later use.\n\nJS - Parse Validation Output safely extracts the AI JSON with a fallback so a parse error never blocks a legitimate booking.\n\nFilter - Valid Bookings Only drops anything scoring below 5 \u2014 protecting downstream nodes from spam or nonsense requests."
      },
      "typeVersion": 1
    },
    {
      "id": "db5294e7-56e3-49cd-82b9-fef53e360231",
      "name": "Sticky Note - Stage 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5088,
        -448
      ],
      "parameters": {
        "color": 4,
        "width": 1120,
        "height": 800,
        "content": "## Stage 2: Seat Availability & Pricing\n\nA short Wait buffer respects API rate limits before fetching the live seat map from the SeatMap tab in Google Sheets.\n\nJS - Check Availability & Assign Seats finds the row matching the show, date, and seat category, reads the available seat count and next seat number, and assigns specific seat labels (e.g. S1, S2, V1). If the sheet is not yet configured, it defaults to 40 available seats for testing.\n\nFilter - Seats Available drops requests where the requested ticket count exceeds what remains \u2014 no overbooking.\n\nJS - Calculate Ticket Pricing applies base price by category, a 10% bulk discount for 4+ tickets, and 8% tax to produce the final totalPrice."
      },
      "typeVersion": 1
    },
    {
      "id": "e08a53fb-193e-4054-a294-6d6b59e5cdd1",
      "name": "Sticky Note - Stage 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6240,
        -576
      ],
      "parameters": {
        "color": 4,
        "width": 964,
        "height": 1000,
        "content": "## Stage 3: AI Confirmation Message\n\nA second Wait buffer before the confirmation AI call prevents rate limit errors after the pricing calculation.\n\nThe AI confirmation node (claude-sonnet-4, temperature 0.4 for warmth) receives the full booking details including assigned seats, total price, and the upsell suggestions from Stage 1. It returns three structured fields: a personalized confirmation message under 150 words, a single upsell offer line, and arrival instructions.\n\nJS - Parse Confirmation Output extracts the structured JSON safely, with a complete fallback that builds all three fields from raw booking data if AI parsing fails.\n\nA final Wait node allows any async processing to settle before the booking record is assembled."
      },
      "typeVersion": 1
    },
    {
      "id": "44e30558-57dc-445e-938c-4e613e794168",
      "name": "Sticky Note - Stage 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7232,
        -608
      ],
      "parameters": {
        "color": 4,
        "width": 688,
        "height": 1064,
        "content": "## Stage 4: Log, Deliver Ticket & Respond\n\nJS - Format Final Booking Record assembles the complete booking object including a QR payload string (bookingId|showName|showDate|showTime|seats) and the summaryRow array ready for Google Sheets.\n\nThree parallel actions fire simultaneously:\n- Save Booking to Google Sheets Tracker appends the full booking row to the BookingTracker tab\n- Send Confirmation Email sends the confirmation message, assigned seats, pricing, arrival instructions, and QR payload to the customer via SendGrid\n- Send SMS Notification sends a concise booking summary to the customer's phone via Twilio\n\nRespond to Webhook returns the full booking confirmation JSON to the original caller \u2014 bookingId, show details, assigned seats, total price, and status."
      },
      "typeVersion": 1
    },
    {
      "id": "7ca744e7-21e2-4269-860b-2a75b903663a",
      "name": "Webhook - Receive Booking Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        3904,
        -16
      ],
      "parameters": {
        "path": "theater-booking-inbound",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "2f38ee15-edd3-4bfb-a878-52a1d2532304",
      "name": "Prepare Booking Context",
      "type": "n8n-nodes-base.set",
      "position": [
        4128,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "showName",
              "type": "string",
              "value": "={{ $json.showName || $json.body?.showName || 'The Phantom of the Opera' }}"
            },
            {
              "name": "showDate",
              "type": "string",
              "value": "={{ $json.showDate || $json.body?.showDate || new Date(Date.now() + 7*86400000).toISOString().split('T')[0] }}"
            },
            {
              "name": "showTime",
              "type": "string",
              "value": "={{ $json.showTime || $json.body?.showTime || '19:30' }}"
            },
            {
              "name": "seatCategory",
              "type": "string",
              "value": "={{ $json.seatCategory || $json.body?.seatCategory || 'Standard' }}"
            },
            {
              "name": "ticketCount",
              "type": "number",
              "value": "={{ $json.ticketCount || $json.body?.ticketCount || 2 }}"
            },
            {
              "name": "customerName",
              "type": "string",
              "value": "={{ $json.customerName || $json.body?.customerName || 'Guest' }}"
            },
            {
              "name": "customerEmail",
              "type": "string",
              "value": "={{ $json.customerEmail || $json.body?.customerEmail || 'guest@example.com' }}"
            },
            {
              "name": "customerPhone",
              "type": "string",
              "value": "={{ $json.customerPhone || $json.body?.customerPhone || '' }}"
            },
            {
              "name": "specialRequests",
              "type": "string",
              "value": "={{ $json.specialRequests || $json.body?.specialRequests || 'None' }}"
            },
            {
              "name": "submittedAt",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            },
            {
              "name": "bookingId",
              "type": "string",
              "value": "={{ 'TIX-' + Date.now().toString().slice(-8) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "329e6ae1-e65e-4395-8735-abb5f564d0de",
      "name": "AI - Validate & Normalize Booking",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4352,
        -16
      ],
      "parameters": {
        "text": "=You are a meticulous theater box office assistant.\n\nA customer has submitted the following booking request:\nShow: {{ $json.showName }}\nDate: {{ $json.showDate }}\nTime: {{ $json.showTime }}\nSeat Category: {{ $json.seatCategory }}\nTicket Count: {{ $json.ticketCount }}\nSpecial Requests: {{ $json.specialRequests }}\n\nYour task: validate and normalize this request. Return a JSON object with EXACTLY these fields:\n{\n  \"isValidBooking\": true or false,\n  \"validationNotes\": \"Brief explanation of any issues found\",\n  \"normalizedShowName\": \"Cleaned up show title\",\n  \"normalizedShowDate\": \"YYYY-MM-DD\",\n  \"normalizedShowTime\": \"HH:MM 24hr format\",\n  \"normalizedSeatCategory\": \"VIP|Premium|Standard\",\n  \"ticketCountReasonable\": true or false,\n  \"upsellSuggestions\": [\"suggestion 1\", \"suggestion 2\"],\n  \"validationScore\": <number 1-10, 10 = perfectly valid and reasonable request>\n}\n\nReturn ONLY the JSON. No markdown, no explanation.",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "6968cda2-92e0-4c3a-bbc5-ad14160c64cf",
      "name": "JS - Parse Validation Output",
      "type": "n8n-nodes-base.code",
      "position": [
        4704,
        -16
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nlet parsed = {};\ntry {\n  const rawText = item.output || item.text || item.response || '{}';\n  const clean = rawText.replace(/```json|```/g, '').trim();\n  parsed = JSON.parse(clean);\n} catch(e) {\n  parsed = {\n    isValidBooking: true,\n    validationNotes: 'Auto-passed: could not parse AI validation output',\n    normalizedShowName: item.showName,\n    normalizedShowDate: item.showDate,\n    normalizedShowTime: item.showTime,\n    normalizedSeatCategory: item.seatCategory,\n    ticketCountReasonable: true,\n    upsellSuggestions: [],\n    validationScore: 5\n  };\n}\nreturn {\n  json: {\n    ...item,\n    ...parsed\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "563f17cb-041a-41b8-9074-b74b8fc8f127",
      "name": "Filter - Valid Bookings Only",
      "type": "n8n-nodes-base.filter",
      "position": [
        4928,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.validationScore }}",
              "rightValue": 5
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e292c7f3-c82f-4b9a-b99d-948d893277ff",
      "name": "Fetch Seat Map from Google Sheets",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5376,
        -16
      ],
      "parameters": {
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/SeatMap!A1:F500",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api"
      },
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2e473668-3d73-4c51-bae2-e56d2767760b",
      "name": "JS - Check Availability & Assign Seats",
      "type": "n8n-nodes-base.code",
      "position": [
        5600,
        -16
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst rows = item.values || [];\n\n// Find the row matching this show/date/time/category: [showName, showDate, showTime, seatCategory, availableSeats, nextSeatNumberStart]\nconst matchRow = rows.find(r =>\n  (r[0] || '').toLowerCase() === (item.normalizedShowName || '').toLowerCase() &&\n  r[1] === item.normalizedShowDate &&\n  r[3] === item.normalizedSeatCategory\n);\n\nconst availableSeats = matchRow ? parseInt(matchRow[4], 10) : 40; // default mock availability if sheet not configured yet\nconst nextSeatStart = matchRow ? parseInt(matchRow[5], 10) : 1;\nconst ticketCount = item.ticketCount || 1;\n\nconst seatsAvailable = availableSeats >= ticketCount;\nconst assignedSeats = [];\nif (seatsAvailable) {\n  for (let i = 0; i < ticketCount; i++) {\n    assignedSeats.push(`${item.normalizedSeatCategory.charAt(0).toUpperCase()}${nextSeatStart + i}`);\n  }\n}\n\nreturn {\n  json: {\n    ...item,\n    seatsAvailable,\n    remainingSeatsBeforeBooking: availableSeats,\n    assignedSeats\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "1fb9e9b5-a9e2-4b7a-a8d4-4fd5558ef1f5",
      "name": "Filter - Seats Available",
      "type": "n8n-nodes-base.filter",
      "position": [
        5824,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.seatsAvailable }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "be783f7d-026c-46da-b834-e41e1cfebadf",
      "name": "JS - Calculate Ticket Pricing",
      "type": "n8n-nodes-base.code",
      "position": [
        6048,
        -16
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst basePrices = { VIP: 150, Premium: 90, Standard: 55 };\nconst category = item.normalizedSeatCategory || 'Standard';\nconst basePrice = basePrices[category] || basePrices.Standard;\nconst ticketCount = item.ticketCount || 1;\n\nlet subtotal = basePrice * ticketCount;\nconst bulkDiscountRate = ticketCount >= 4 ? 0.10 : 0;\nconst discountAmount = subtotal * bulkDiscountRate;\nconst afterDiscount = subtotal - discountAmount;\nconst taxRate = 0.08;\nconst taxAmount = afterDiscount * taxRate;\nconst totalPrice = Math.round((afterDiscount + taxAmount) * 100) / 100;\n\nreturn {\n  json: {\n    ...item,\n    basePricePerSeat: basePrice,\n    subtotal: Math.round(subtotal * 100) / 100,\n    bulkDiscountRate,\n    discountAmount: Math.round(discountAmount * 100) / 100,\n    taxAmount: Math.round(taxAmount * 100) / 100,\n    totalPrice\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "0e0bb9f8-3868-4130-bf7a-e1de77ff9fef",
      "name": "AI - Generate Confirmation Message",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        6496,
        -16
      ],
      "parameters": {
        "text": "=You are a friendly theater box office assistant writing a booking confirmation.\n\nBooking details:\nShow: {{ $json.normalizedShowName }}\nDate: {{ $json.normalizedShowDate }}\nTime: {{ $json.normalizedShowTime }}\nSeat Category: {{ $json.normalizedSeatCategory }}\nAssigned Seats: {{ JSON.stringify($json.assignedSeats) }}\nTicket Count: {{ $json.ticketCount }}\nTotal Price: ${{ $json.totalPrice }}\nCustomer: {{ $json.customerName }}\nBooking Reference: {{ $json.bookingId }}\nUpsell Ideas: {{ JSON.stringify($json.upsellSuggestions) }}\n\nReturn a JSON object with EXACTLY these fields:\n{\n  \"confirmationMessage\": \"Warm, concise confirmation under 150 words thanking the customer by name, restating show/date/time/seats/total, and mentioning the booking reference\",\n  \"upsellOffer\": \"One short, tasteful upsell line (snacks, parking, merchandise) based on the upsell ideas\",\n  \"arrivalInstructions\": \"One or two sentences on arriving 20-30 minutes early with the booking reference or QR code\"\n}\n\nReturn ONLY the JSON. No markdown.",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "108f2e6b-3783-41bc-bd3b-8a1f7a480945",
      "name": "JS - Parse Confirmation Output",
      "type": "n8n-nodes-base.code",
      "position": [
        6848,
        -16
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nlet parsed = {};\ntry {\n  const rawText = item.output || item.text || item.response || '{}';\n  const clean = rawText.replace(/```json|```/g, '').trim();\n  parsed = JSON.parse(clean);\n} catch(e) {\n  parsed = {\n    confirmationMessage: `Hi ${item.customerName}, your booking for ${item.normalizedShowName} on ${item.normalizedShowDate} at ${item.normalizedShowTime} is confirmed. Seats: ${(item.assignedSeats || []).join(', ')}. Total: $${item.totalPrice}. Reference: ${item.bookingId}.`,\n    upsellOffer: 'Add pre-show snacks or reserved parking at checkout next time!',\n    arrivalInstructions: 'Please arrive 20-30 minutes early with your booking reference.'\n  };\n}\nreturn {\n  json: {\n    ...item,\n    ...parsed\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f2da0d7d-bc00-4908-b426-89ad2c36fa1e",
      "name": "JS - Format Final Booking Record",
      "type": "n8n-nodes-base.code",
      "position": [
        7280,
        -16
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst today = new Date().toISOString().split('T')[0];\nconst qrPayload = `${item.bookingId}|${item.normalizedShowName}|${item.normalizedShowDate}|${item.normalizedShowTime}|${(item.assignedSeats || []).join(',')}`;\n\nreturn {\n  json: {\n    ...item,\n    qrPayload,\n    status: 'Confirmed',\n    exportDate: today,\n    summaryRow: [\n      today,\n      item.bookingId || 'N/A',\n      item.normalizedShowName || 'Unknown',\n      item.normalizedShowDate || '',\n      item.normalizedShowTime || '',\n      item.normalizedSeatCategory || '',\n      (item.assignedSeats || []).join(', '),\n      (item.ticketCount || 0).toString(),\n      ('$' + (item.totalPrice || 0).toString()),\n      item.customerName || '',\n      item.customerEmail || '',\n      item.status || 'Confirmed',\n      new Date().toISOString()\n    ]\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "6742a13f-1075-4559-b942-5a2fd7d211e7",
      "name": "Save Booking to Google Sheets Tracker",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        7504,
        -208
      ],
      "parameters": {
        "url": "https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/BookingTracker!A1:append?valueInputOption=USER_ENTERED",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"values\": [{{ JSON.stringify($json.summaryRow) }}]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api"
      },
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0f5dc8dd-712e-4301-bc93-1d9b0819f45d",
      "name": "Send Confirmation Email",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        7504,
        -16
      ],
      "parameters": {
        "url": "https://api.sendgrid.com/v3/mail/send",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"personalizations\": [{\"to\": [{\"email\": \"{{ $json.customerEmail }}\"}]}],\n  \"from\": {\"email\": \"boxoffice@yourtheater.com\", \"name\": \"Theater Box Office\"},\n  \"subject\": \"Your Tickets Are Confirmed: {{ $json.normalizedShowName }} ({{ $json.bookingId }})\",\n  \"content\": [{\n    \"type\": \"text/plain\",\n    \"value\": \"{{ $json.confirmationMessage }}\\n\\nSeats: {{ JSON.stringify($json.assignedSeats) }}\\nTotal Paid: ${{ $json.totalPrice }}\\n\\n{{ $json.arrivalInstructions }}\\n\\n{{ $json.upsellOffer }}\\n\\nTicket QR payload: {{ $json.qrPayload }}\"\n  }]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "bad26571-2294-4f66-a46e-5aa1f35389e7",
      "name": "Send SMS Notification",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        7504,
        176
      ],
      "parameters": {
        "url": "=https://api.twilio.com/2010-04-01/Accounts/YOUR_TWILIO_ACCOUNT_SID/Messages.json",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"To\": \"{{ $json.customerPhone }}\",\n  \"From\": \"YOUR_TWILIO_PHONE_NUMBER\",\n  \"Body\": \"{{ $json.normalizedShowName }} confirmed! Seats {{ JSON.stringify($json.assignedSeats) }} on {{ $json.normalizedShowDate }} at {{ $json.normalizedShowTime }}. Ref: {{ $json.bookingId }}. Total: ${{ $json.totalPrice }}\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Basic YOUR_TWILIO_BASE64_AUTH"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "11aeb223-922b-4d4e-bd97-9d79ee0ea26f",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        7728,
        -208
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"bookingId\": \"{{ $json.bookingId }}\",\n  \"showName\": \"{{ $json.normalizedShowName }}\",\n  \"showDate\": \"{{ $json.normalizedShowDate }}\",\n  \"showTime\": \"{{ $json.normalizedShowTime }}\",\n  \"assignedSeats\": {{ JSON.stringify($json.assignedSeats) }},\n  \"totalPrice\": {{ $json.totalPrice }},\n  \"status\": \"{{ $json.status }}\",\n  \"message\": \"Booking confirmed. Confirmation email and SMS sent.\"\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "689c6dfb-f231-4826-ae19-73bbe745300f",
      "name": "Anthropic - Validation Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        4432,
        208
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-20250514"
        },
        "options": {
          "temperature": 0.2
        }
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "a1675896-8076-486e-bad2-8ef7b4f9b9d9",
      "name": "Anthropic - Confirmation Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        6576,
        208
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-20250514"
        },
        "options": {
          "temperature": 0.4
        }
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "202fac9a-183a-4a37-a9c4-c214be0585fb",
      "name": "Wait - API Rate Limit",
      "type": "n8n-nodes-base.wait",
      "position": [
        5152,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "af04b952-7de7-4bc7-8a6d-7e0336ec2c69",
      "name": "Wait for API ",
      "type": "n8n-nodes-base.wait",
      "position": [
        6272,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a25d0925-34c9-4d1e-964f-6223aab60f3f",
      "name": "Wait for API Result",
      "type": "n8n-nodes-base.wait",
      "position": [
        7056,
        -16
      ],
      "parameters": {},
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "79361680-4606-4e4d-af02-f24c72a38902",
  "connections": {
    "Wait for API ": {
      "main": [
        [
          {
            "node": "AI - Generate Confirmation Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for API Result": {
      "main": [
        [
          {
            "node": "JS - Format Final Booking Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - API Rate Limit": {
      "main": [
        [
          {
            "node": "Fetch Seat Map from Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Booking Context": {
      "main": [
        [
          {
            "node": "AI - Validate & Normalize Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter - Seats Available": {
      "main": [
        [
          {
            "node": "JS - Calculate Ticket Pricing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic - Validation Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Validate & Normalize Booking",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Filter - Valid Bookings Only": {
      "main": [
        [
          {
            "node": "Wait - API Rate Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Parse Validation Output": {
      "main": [
        [
          {
            "node": "Filter - Valid Bookings Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Calculate Ticket Pricing": {
      "main": [
        [
          {
            "node": "Wait for API ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic - Confirmation Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Generate Confirmation Message",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "JS - Parse Confirmation Output": {
      "main": [
        [
          {
            "node": "Wait for API Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Format Final Booking Record": {
      "main": [
        [
          {
            "node": "Save Booking to Google Sheets Tracker",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Confirmation Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send SMS Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Validate & Normalize Booking": {
      "main": [
        [
          {
            "node": "JS - Parse Validation Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Seat Map from Google Sheets": {
      "main": [
        [
          {
            "node": "JS - Check Availability & Assign Seats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Booking Request": {
      "main": [
        [
          {
            "node": "Prepare Booking Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Generate Confirmation Message": {
      "main": [
        [
          {
            "node": "JS - Parse Confirmation Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Booking to Google Sheets Tracker": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Check Availability & Assign Seats": {
      "main": [
        [
          {
            "node": "Filter - Seats Available",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}