This workflow corresponds to n8n.io template #10223 — we link there as the canonical source.
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 →
{
"id": "XOs7YXaXmcGasosl",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Dynamic Seating & Venue Layout Planner",
"tags": [],
"nodes": [
{
"id": "6ebed595-6ce0-4738-9350-639fe6eaa8dc",
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"position": [
-1584,
336
],
"parameters": {
"path": "seating-planner",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2
},
{
"id": "f9099584-6f17-4392-ba9f-4819eefc857c",
"name": "Sticky Note - Trigger",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1648,
-112
],
"parameters": {
"color": 4,
"width": 208,
"height": 696,
"content": "## Trigger Seating Request\n\nReceives venue requirements and attendee data via webhook.\n\nExpected payload:\n{\n \"eventType\": \"Conference/Wedding/Banquet\",\n \"venueCapacity\": 500,\n \"attendeeCount\": 350,\n \"layoutPreference\": \"Theater/Classroom/Banquet\"\n}"
},
"typeVersion": 1
},
{
"id": "c7e2cfc4-5c30-4a37-9481-b08a7c338759",
"name": "Validate Request Data",
"type": "n8n-nodes-base.code",
"position": [
-1360,
336
],
"parameters": {
"jsCode": "// Validate and parse incoming request\nconst payload = $input.first().json.body || $input.first().json;\n\nconst requestData = {\n eventType: payload.eventType || \"Conference\",\n venueCapacity: parseInt(payload.venueCapacity) || 500,\n attendeeCount: parseInt(payload.attendeeCount) || 350,\n layoutPreference: payload.layoutPreference || \"Theater\",\n venueDimensions: payload.venueDimensions || { width: 30, length: 40 }, // in meters\n specialRequirements: payload.specialRequirements || [],\n accessibilityNeeds: parseInt(payload.accessibilityNeeds) || 10,\n vipCount: parseInt(payload.vipCount) || 0,\n requestId: payload.requestId || `seat-${Date.now()}`,\n timestamp: new Date().toISOString()\n};\n\n// Validate capacity\nif (requestData.attendeeCount > requestData.venueCapacity) {\n return {\n json: {\n error: true,\n message: \"Attendee count exceeds venue capacity\",\n requestData\n }\n };\n}\n\nreturn { json: requestData };"
},
"typeVersion": 2
},
{
"id": "493e8a46-496a-4169-a6bf-6527169feba3",
"name": "Fetch Attendee Data",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1136,
192
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Attendees"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SPREADSHEET_ID",
"cachedResultName": "Venue Data"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "b93eb9e9-f6fa-451c-aec8-c510b30fc57f",
"name": "Sticky Note - Fetch Data",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1408,
-112
],
"parameters": {
"color": 3,
"width": 176,
"height": 688,
"content": "## Fetch Attendee Data\n\nRetrieve attendee information including names, groups, accessibility needs, and VIP status from Google Sheets."
},
"typeVersion": 1
},
{
"id": "afa08e18-f492-4d0a-a818-91c914180cf3",
"name": "Fetch Venue Templates",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1136,
480
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=1",
"cachedResultName": "Venue Layouts"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SPREADSHEET_ID",
"cachedResultName": "Venue Data"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "3134f27d-2601-4599-a4d7-4627c06833a3",
"name": "Combine All Data",
"type": "n8n-nodes-base.code",
"position": [
-912,
336
],
"parameters": {
"jsCode": "// Combine all data for processing\nconst requestData = $input.all()[0].json;\nconst attendees = $input.all()[1].json;\nconst venueTemplates = $input.all()[2].json;\n\n// Structure combined data\nconst combinedData = {\n request: requestData,\n attendees: Array.isArray(attendees) ? attendees : [attendees],\n venueTemplates: Array.isArray(venueTemplates) ? venueTemplates : [venueTemplates],\n processingTimestamp: new Date().toISOString()\n};\n\nreturn { json: combinedData };"
},
"typeVersion": 2
},
{
"id": "fcc3ef45-7cea-4b8f-976d-34a945785bdb",
"name": "Sticky Note - Calculate",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1168,
-192
],
"parameters": {
"color": 5,
"width": 192,
"height": 864,
"content": "## Calculate Totals\n\nAggregate attendee data, venue constraints, and layout requirements for optimal planning."
},
"typeVersion": 1
},
{
"id": "55f73cc6-3003-4ff9-b202-e333b86afdcb",
"name": "Optimize Seating Layout",
"type": "n8n-nodes-base.code",
"position": [
-688,
336
],
"parameters": {
"jsCode": "const data = $input.first().json;\nconst request = data.request;\nconst attendees = data.attendees;\nconst templates = data.venueTemplates;\n\n// Select appropriate layout template\nfunction selectLayoutTemplate(eventType, layoutPref, templates) {\n const template = templates.find(t => \n t.eventType === eventType && t.layoutType === layoutPref\n ) || templates[0] || {\n layoutType: layoutPref,\n seatsPerRow: 20,\n rowSpacing: 1.2,\n aisleWidth: 1.5,\n stageDepth: 5\n };\n \n return template;\n}\n\n// Calculate optimal layout dimensions\nfunction calculateLayout(attendeeCount, venueSize, template) {\n const seatsPerRow = template.seatsPerRow || 20;\n const totalRows = Math.ceil(attendeeCount / seatsPerRow);\n const rowSpacing = template.rowSpacing || 1.2;\n const aisleWidth = template.aisleWidth || 1.5;\n \n // Calculate number of aisles needed\n const aislesNeeded = Math.floor(seatsPerRow / 10);\n \n // Calculate actual width needed\n const seatWidth = 0.6; // meters\n const widthNeeded = (seatsPerRow * seatWidth) + (aislesNeeded * aisleWidth);\n const lengthNeeded = (totalRows * rowSpacing) + (template.stageDepth || 5);\n \n return {\n totalRows,\n seatsPerRow,\n aislesNeeded,\n dimensions: {\n width: widthNeeded,\n length: lengthNeeded\n },\n feasible: widthNeeded <= venueSize.width && lengthNeeded <= venueSize.length\n };\n}\n\n// Group attendees by type\nfunction categorizeAttendees(attendees) {\n const vips = attendees.filter(a => a.vipStatus === true || a.type === 'VIP');\n const accessibility = attendees.filter(a => a.accessibility === true || a.accessibilityNeeds);\n const groups = {};\n \n attendees.forEach(a => {\n const group = a.group || a.company || 'General';\n if (!groups[group]) groups[group] = [];\n groups[group].push(a);\n });\n \n return { vips, accessibility, groups };\n}\n\n// Generate seating assignments\nfunction generateSeatingPlan(layout, categorized, totalAttendees) {\n const seatingPlan = [];\n let currentRow = 1;\n let currentSeat = 1;\n \n // Assign VIPs to front rows\n categorized.vips.forEach((vip, index) => {\n seatingPlan.push({\n attendeeId: vip.id || vip.name,\n attendeeName: vip.name,\n section: 'VIP',\n row: Math.floor(index / layout.seatsPerRow) + 1,\n seat: (index % layout.seatsPerRow) + 1,\n type: 'VIP',\n notes: vip.notes || ''\n });\n });\n \n // Reserve accessible seating near aisles\n let accessibleSeatsAssigned = 0;\n categorized.accessibility.forEach((person, index) => {\n const rowNum = Math.floor(layout.totalRows / 2) + Math.floor(index / 2);\n seatingPlan.push({\n attendeeId: person.id || person.name,\n attendeeName: person.name,\n section: 'Accessible',\n row: rowNum,\n seat: index % 2 === 0 ? 1 : layout.seatsPerRow, // Aisle seats\n type: 'Accessible',\n notes: person.accessibilityNeeds || 'Wheelchair accessible'\n });\n accessibleSeatsAssigned++;\n });\n \n // Assign groups together\n let generalSeatCounter = categorized.vips.length;\n Object.keys(categorized.groups).forEach(groupName => {\n const groupMembers = categorized.groups[groupName];\n \n groupMembers.forEach((member, index) => {\n // Skip if already assigned (VIP or accessible)\n if (seatingPlan.find(s => s.attendeeId === (member.id || member.name))) {\n return;\n }\n \n const rowNum = Math.floor(generalSeatCounter / layout.seatsPerRow) + 1;\n const seatNum = (generalSeatCounter % layout.seatsPerRow) + 1;\n \n seatingPlan.push({\n attendeeId: member.id || member.name,\n attendeeName: member.name,\n section: 'General',\n row: rowNum,\n seat: seatNum,\n type: 'Group',\n group: groupName,\n notes: member.notes || ''\n });\n \n generalSeatCounter++;\n });\n });\n \n return seatingPlan;\n}\n\n// Main processing\nconst template = selectLayoutTemplate(\n request.eventType,\n request.layoutPreference,\n templates\n);\n\nconst layout = calculateLayout(\n request.attendeeCount,\n request.venueDimensions,\n template\n);\n\nconst categorized = categorizeAttendees(attendees);\nconst seatingPlan = generateSeatingPlan(layout, categorized, request.attendeeCount);\n\n// Calculate statistics\nconst stats = {\n totalSeats: layout.totalRows * layout.seatsPerRow,\n assignedSeats: seatingPlan.length,\n availableSeats: (layout.totalRows * layout.seatsPerRow) - seatingPlan.length,\n utilizationRate: ((seatingPlan.length / (layout.totalRows * layout.seatsPerRow)) * 100).toFixed(2),\n vipSeats: categorized.vips.length,\n accessibleSeats: categorized.accessibility.length,\n groupCount: Object.keys(categorized.groups).length\n};\n\n// Generate output\nconst result = {\n requestId: request.requestId,\n timestamp: new Date().toISOString(),\n eventType: request.eventType,\n layoutType: request.layoutPreference,\n venue: {\n capacity: request.venueCapacity,\n dimensions: request.venueDimensions,\n feasible: layout.feasible\n },\n layout: {\n totalRows: layout.totalRows,\n seatsPerRow: layout.seatsPerRow,\n aisles: layout.aislesNeeded,\n calculatedDimensions: layout.dimensions\n },\n seatingPlan: seatingPlan,\n statistics: stats,\n visualMap: generateVisualMap(layout, seatingPlan)\n};\n\n// Generate ASCII visual map\nfunction generateVisualMap(layout, seating) {\n const map = [];\n map.push('\\n=== VENUE LAYOUT MAP ===\\n');\n map.push('[STAGE]'.padStart(layout.seatsPerRow * 2, ' '));\n map.push('\\n');\n \n for (let row = 1; row <= layout.totalRows; row++) {\n let rowStr = `R${row.toString().padStart(2, '0')} `;\n \n for (let seat = 1; seat <= layout.seatsPerRow; seat++) {\n const assignment = seating.find(s => s.row === row && s.seat === seat);\n \n if (assignment) {\n if (assignment.type === 'VIP') rowStr += 'V ';\n else if (assignment.type === 'Accessible') rowStr += 'A ';\n else rowStr += 'X ';\n } else {\n rowStr += '\u25cb ';\n }\n \n // Add aisle\n if (seat % 10 === 0 && seat !== layout.seatsPerRow) {\n rowStr += '| ';\n }\n }\n \n map.push(rowStr);\n }\n \n map.push('\\n\\nLegend: V=VIP | A=Accessible | X=Assigned | \u25cb=Available\\n');\n \n return map.join('\\n');\n}\n\nreturn { json: result };"
},
"typeVersion": 2
},
{
"id": "241cbdaf-4e19-4243-8bff-000f8543ed52",
"name": "Sticky Note - Optimize",
"type": "n8n-nodes-base.stickyNote",
"position": [
-928,
-112
],
"parameters": {
"width": 352,
"height": 752,
"content": "## AI Optimization\n\nUse algorithms to calculate optimal seating based on:\n- Venue dimensions\n- Attendee groups\n- Accessibility needs\n- VIP placement\n- Aisle placement"
},
"typeVersion": 1
},
{
"id": "cd660d9f-7d3c-4593-8ae9-d854f6cbc514",
"name": "Format Recommendations",
"type": "n8n-nodes-base.code",
"position": [
-464,
336
],
"parameters": {
"jsCode": "const layoutData = $input.first().json;\n\n// Format recommendations in natural language\nconst recommendations = [];\n\nif (!layoutData.venue.feasible) {\n recommendations.push('\u26a0\ufe0f WARNING: Current layout exceeds venue dimensions. Consider reducing seats per row or attendee count.');\n}\n\nif (layoutData.statistics.utilizationRate > 95) {\n recommendations.push('\u26a0\ufe0f High capacity utilization. Consider adding buffer space for comfort.');\n}\n\nif (layoutData.statistics.utilizationRate < 70) {\n recommendations.push('\u2713 Good space utilization with room for comfort and movement.');\n}\n\nif (layoutData.layout.aisles < 2) {\n recommendations.push('\ud83d\udca1 Consider adding more aisles for better traffic flow.');\n}\n\nif (layoutData.statistics.accessibleSeats > 0) {\n recommendations.push(`\u2713 ${layoutData.statistics.accessibleSeats} accessible seats reserved near aisles.`);\n}\n\nif (layoutData.statistics.vipSeats > 0) {\n recommendations.push(`\u2713 ${layoutData.statistics.vipSeats} VIP seats assigned in front rows.`);\n}\n\nrecommendations.push(`\u2713 ${layoutData.statistics.groupCount} groups seated together for better networking.`);\n\n// Create formatted output\nconst formattedOutput = {\n ...layoutData,\n recommendations: recommendations,\n summary: `Successfully generated seating plan for ${layoutData.statistics.assignedSeats} attendees in ${layoutData.layout.totalRows} rows with ${layoutData.statistics.availableSeats} buffer seats.`,\n exportReady: true\n};\n\nreturn { json: formattedOutput };"
},
"typeVersion": 2
},
{
"id": "2e43af7a-7d3c-4077-b15d-b86a0b83a962",
"name": "Sticky Note - Format",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
32
],
"parameters": {
"color": 3,
"height": 528,
"content": "## Format Recommendations\n\nStructure seating plan with:\n- Visual layout map\n- Seat assignments\n- Statistics & metrics\n- Optimization tips"
},
"typeVersion": 1
},
{
"id": "3b5e5ec2-317b-4977-bdbf-0b3536e20b3e",
"name": "Save Master Plan",
"type": "n8n-nodes-base.googleSheets",
"position": [
-224,
96
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=2",
"cachedResultName": "Seating Plans"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SPREADSHEET_ID",
"cachedResultName": "Venue Data"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "acfda3e7-b8ab-4d29-aba4-a616f3fb2cdb",
"name": "Save Individual Assignments",
"type": "n8n-nodes-base.googleSheets",
"position": [
-16,
528
],
"parameters": {
"columns": {
"mappingMode": "autoMapInputData"
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=3",
"cachedResultName": "Seat Assignments"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SPREADSHEET_ID",
"cachedResultName": "Venue Data"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "6cdcdd87-3938-46f7-856e-47da5213884d",
"name": "Sticky Note - Save",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
288
],
"parameters": {
"color": 2,
"width": 368,
"height": 436,
"content": "## Update Sheets\n\nSave seating plan to Google Sheets:\n- Master plan summary\n- Individual seat assignments\n- Layout specifications"
},
"typeVersion": 1
},
{
"id": "9f0f8821-d72a-440b-958d-2cf4ee20f553",
"name": "Send Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-224,
-80
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json, null, 2) }}"
},
"typeVersion": 1
},
{
"id": "126e21b6-1913-452d-bd8e-c65c4109aab5",
"name": "Sticky Note - Response",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
-304
],
"parameters": {
"color": 5,
"width": 272,
"height": 576,
"content": "## Send Alert\n\nReturn complete seating plan with:\n- Visual layout map\n- Seat assignments list\n- Statistics & recommendations\n- Export-ready format"
},
"typeVersion": 1
},
{
"id": "7ae9a9d9-a170-4a1f-aca5-37d393ba9938",
"name": "Split Seat Assignments",
"type": "n8n-nodes-base.code",
"position": [
-240,
528
],
"parameters": {
"jsCode": "const layoutData = $input.first().json;\n\n// Split seating plan into individual seat records\nconst seatAssignments = layoutData.seatingPlan.map(seat => ({\n requestId: layoutData.requestId,\n timestamp: layoutData.timestamp,\n attendeeId: seat.attendeeId,\n attendeeName: seat.attendeeName,\n section: seat.section,\n row: seat.row,\n seat: seat.seat,\n type: seat.type,\n group: seat.group || '',\n notes: seat.notes || ''\n}));\n\nreturn seatAssignments.map(seat => ({ json: seat }));"
},
"typeVersion": 2
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "d1d5db44-9513-485d-8c2b-4a480fe4190e",
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Validate Request Data",
"type": "main",
"index": 0
}
]
]
},
"Combine All Data": {
"main": [
[
{
"node": "Optimize Seating Layout",
"type": "main",
"index": 0
}
]
]
},
"Fetch Attendee Data": {
"main": [
[
{
"node": "Combine All Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Venue Templates": {
"main": [
[
{
"node": "Combine All Data",
"type": "main",
"index": 0
}
]
]
},
"Validate Request Data": {
"main": [
[
{
"node": "Fetch Attendee Data",
"type": "main",
"index": 0
},
{
"node": "Fetch Venue Templates",
"type": "main",
"index": 0
},
{
"node": "Combine All Data",
"type": "main",
"index": 0
}
]
]
},
"Format Recommendations": {
"main": [
[
{
"node": "Send Response",
"type": "main",
"index": 0
},
{
"node": "Save Master Plan",
"type": "main",
"index": 0
},
{
"node": "Split Seat Assignments",
"type": "main",
"index": 0
}
]
]
},
"Split Seat Assignments": {
"main": [
[
{
"node": "Save Individual Assignments",
"type": "main",
"index": 0
}
]
]
},
"Optimize Seating Layout": {
"main": [
[
{
"node": "Format Recommendations",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Enhance event logistics with this automated n8n workflow. Triggered by seating requests, it fetches attendee data and venue templates from Google Sheets, calculates totals, and optimizes seating layouts. The workflow generates detailed recommendations, splits individual…
Source: https://n8n.io/workflows/10223/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
[SANTOBET] FLUXO TODO - BACKUP. Uses googleSheets, httpRequest, googleSheetsTrigger. Webhook trigger; 57 nodes.
FLUXO DISPARO DATA E HORA. Uses itemLists, googleSheets, httpRequest. Webhook trigger; 48 nodes.
This workflow allows you to accept online payments via YooKassa and log both orders and transactions in Google Sheets — all without writing a single line of code. It supports full payment flow: produc
Transform your n8n instance management with this advanced automation system featuring artificial intelligence-driven workflow selection. This template provides comprehensive maintenance operations wit
Nexus_v6(ล่าสุดจริงๆ)ล่าสุดไกไก. Uses googleSheets, httpRequest. Webhook trigger; 41 nodes.