{
  "id": "",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Direct Booking Site Generator",
  "tags": [],
  "nodes": [
    {
      "id": "911dcafa-b919-400e-b11c-dd5ef469c244",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        -720
      ],
      "parameters": {
        "width": 1968,
        "height": 352,
        "content": "## \ud83c\udfe0 Direct Booking Site Generator\n\nThis workflow creates a professional direct booking website from any Airbnb listing and publishes it to Netlify for free.\n\n### How it works\n1. Enter your Airbnb listing ID\n2. The scraper fetches all listing data\n3. A beautiful static site is generated\n4. Site is deployed to Netlify automatically\n\n### Required Credentials\n- **Airbnb Scraper API** - Get your token at [shortrentals.ai](https://scraper.shortrentals.ai)\n- **Netlify API** - Create a personal access token at [Netlify](https://app.netlify.com/user/applications#personal-access-tokens)"
      },
      "typeVersion": 1
    },
    {
      "id": "75e9e3a1-4cec-43c6-912b-5c56914c50fa",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        -336
      ],
      "parameters": {
        "width": 416,
        "height": 256,
        "content": "## \u2699\ufe0f Configuration\n\nEdit the **listingId** field below with your Airbnb listing ID.\n\nFind it in the URL:\n`airbnb.com/rooms/`**1234567890**"
      },
      "typeVersion": 1
    },
    {
      "id": "325d5852-bd4a-4037-9d9c-7ece2de50695",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        -336
      ],
      "parameters": {
        "width": 320,
        "height": 256,
        "content": "## \ud83d\udd0d Scrape Data\n\nFetches comprehensive listing data including:\n- Title & description\n- Photos\n- Amenities\n- Pricing\n- Host info\n- Reviews"
      },
      "typeVersion": 1
    },
    {
      "id": "022c3063-e0ca-4249-8144-07714c8db90a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        -336
      ],
      "parameters": {
        "width": 560,
        "height": 256,
        "content": "## \ud83c\udfa8 Generate Site and Zip File\n\nCreates a beautiful, responsive HTML page with:\n- Photo gallery\n- Property details\n- Booking form\n- Host section\n- Mobile-friendly design"
      },
      "typeVersion": 1
    },
    {
      "id": "3d948039-5672-4df8-a130-e54946ed6e94",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        -336
      ],
      "parameters": {
        "width": 576,
        "height": 256,
        "content": "## \ud83d\ude80 Deploy to Netlify\n\nPublishes the site to Netlify's free hosting.\n\nThe site URL will be returned in the final output.\n\n**Note:** First deploy creates a new site. Save the site ID if you want to update it later."
      },
      "typeVersion": 1
    },
    {
      "id": "manual-trigger-1",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1088,
        -48
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "set-params-1",
      "name": "Set Listing ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -800,
        -48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "listingId",
              "name": "listingId",
              "type": "string",
              "value": "1501733424018064312"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "airbnb-scraper-1",
      "name": "Airbnb Scraper",
      "type": "n8n-nodes-airbnb-scraper.airbnbScraper",
      "position": [
        -560,
        -48
      ],
      "parameters": {
        "options": {
          "timeout": 120
        },
        "listingId": "={{ $json.listingId }}",
        "operation": "scrapeListing"
      },
      "credentials": {
        "airbnbScraperApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "code-generate-html-1",
      "name": "Generate HTML Site",
      "type": "n8n-nodes-base.code",
      "position": [
        -288,
        -48
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const payload = $input.first().json || {};\nconst data = payload.data || payload;\n\nconst cleanText = (value, fallback = '') => {\n  if (Array.isArray(value)) return fallback;\n  if (typeof value === 'number') return value.toString();\n  return value || fallback;\n};\n\nconst buildGallery = () => {\n  if (!Array.isArray(data.images) || data.images.length === 0) {\n    return '<div class=\"gallery-item\"><div class=\"placeholder-image\">No photos available</div></div>';\n  }\n  return data.images.slice(0, 6).map((url, index) => '<div class=\"gallery-item\"><img src=\"' + url + '\" alt=\"Property photo ' + (index + 1) + '\" loading=\"lazy\"></div>').join('');\n};\n\nconst formatAmenity = (amenity) => {\n  if (typeof amenity === 'string') return amenity;\n  if (amenity && (amenity.name || amenity.title)) return amenity.name || amenity.title;\n  return '';\n};\n\nconst buildAmenities = () => {\n  if (!Array.isArray(data.amenities) || data.amenities.length === 0) {\n    return '<li><span class=\"amenity-icon\">\u2713</span> Basic amenities</li>';\n  }\n  return data.amenities.slice(0, 12).map((amenity) => formatAmenity(amenity)).filter(Boolean).map((label) => '<li><span class=\"amenity-icon\">\u2713</span> ' + label + '</li>').join('');\n};\n\nconst buildHouseRules = () => {\n  const rules = Array.isArray(data.houseRules) ? data.houseRules.slice(0, 6).map((rule) => {\n    if (typeof rule === 'string') return rule;\n    if (rule && (rule.title || rule.name)) return rule.title || rule.name;\n    return '';\n  }).filter(Boolean) : [];\n  if (!rules.length) {\n    return '<li>Check-in: 3:00 PM</li><li>Checkout: 11:00 AM</li><li>No smoking</li>';\n  }\n  return rules.map((rule) => '<li>' + rule + '</li>').join('');\n};\n\nconst galleryHtml = buildGallery();\nconst amenitiesHtml = buildAmenities();\nconst houseRulesHtml = buildHouseRules();\n\nconst locationParts = [data.location?.city, data.location?.state, data.location?.country].filter(Boolean).join(', ');\nconst price = data.pricing?.pricePerNight || data.price || data.pricing?.basePrice || 100;\nconst currency = data.pricing?.currency || '$';\nconst title = cleanText(data.title, 'Beautiful Vacation Rental');\nconst description = cleanText(data.description, 'A wonderful place to stay');\n\nconst html = '<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>' + title + ' | Direct Booking</title><meta name=\"description\" content=\"' + description.substring(0, 160) + '\"><style>* { margin: 0; padding: 0; box-sizing: border-box; } :root { --primary: #FF5A5F; --primary-dark: #E04850; --text: #222222; --text-light: #717171; --border: #DDDDDD; --background: #FFFFFF; --background-light: #F7F7F7; } body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; color: var(--text); line-height: 1.6; background: var(--background); } .container { max-width: 1120px; margin: 0 auto; padding: 0 24px; } header { border-bottom: 1px solid var(--border); padding: 20px 0; position: sticky; top: 0; background: var(--background); z-index: 100; } .header-content { display: flex; justify-content: space-between; align-items: center; } .logo { font-size: 24px; font-weight: 700; color: var(--primary); text-decoration: none; } .book-now-btn { background: var(--primary); color: white; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: background 0.2s; } .book-now-btn:hover { background: var(--primary-dark); } .gallery { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 24px 0; border-radius: 12px; overflow: hidden; } .gallery-item:first-child { grid-column: span 2; grid-row: span 2; } .gallery-item img { width: 100%; height: 100%; object-fit: cover; min-height: 200px; } .gallery-item:first-child img { min-height: 416px; } .placeholder-image { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; display: flex; align-items: center; justify-content: center; min-height: 200px; font-size: 18px; } .main-content { display: grid; grid-template-columns: 1fr 380px; gap: 48px; padding: 32px 0; } .listing-title { font-size: 26px; font-weight: 600; margin-bottom: 8px; } .listing-location { color: var(--text-light); margin-bottom: 16px; } .listing-stats { display: flex; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; } .stat { display: flex; align-items: center; gap: 6px; } .rating { display: flex; align-items: center; gap: 4px; } .star { color: var(--primary); } .host-section { display: flex; align-items: center; gap: 16px; padding: 24px 0; border-bottom: 1px solid var(--border); } .host-photo { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; background: var(--border); } .host-info h3 { font-size: 16px; font-weight: 600; } .superhost-badge { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-light); margin-top: 4px; } .section { padding: 32px 0; border-bottom: 1px solid var(--border); } .section-title { font-size: 22px; font-weight: 600; margin-bottom: 16px; } .description { color: var(--text); white-space: pre-line; } .amenities-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; } .amenities-grid li { list-style: none; display: flex; align-items: center; gap: 12px; } .amenity-icon { color: var(--primary); font-weight: bold; } .rules-list { list-style: none; } .rules-list li { padding: 8px 0; border-bottom: 1px solid var(--border); } .rules-list li:last-child { border-bottom: none; } .booking-card { position: sticky; top: 100px; background: var(--background); border: 1px solid var(--border); border-radius: 12px; padding: 24px; box-shadow: 0 6px 16px rgba(0,0,0,0.12); } .price-line { display: flex; align-items: baseline; gap: 4px; margin-bottom: 24px; } .price { font-size: 22px; font-weight: 600; } .price-period { color: var(--text-light); } .booking-form { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 16px; } .form-row { display: grid; grid-template-columns: 1fr 1fr; } .form-field { padding: 12px; border-bottom: 1px solid var(--border); } .form-field:nth-child(odd) { border-right: 1px solid var(--border); } .form-field.full-width { grid-column: span 2; border-bottom: none; } .form-field label { display: block; font-size: 10px; font-weight: 600; text-transform: uppercase; margin-bottom: 4px; } .form-field input, .form-field select { width: 100%; border: none; font-size: 14px; background: transparent; outline: none; } .reserve-btn { width: 100%; background: linear-gradient(to right, var(--primary), var(--primary-dark)); color: white; border: none; padding: 14px; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; } .reserve-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(255, 90, 95, 0.4); } .booking-note { text-align: center; color: var(--text-light); font-size: 14px; margin-top: 16px; } footer { background: var(--background-light); padding: 48px 0; margin-top: 48px; } .footer-content { text-align: center; color: var(--text-light); } @media (max-width: 768px) { .main-content { grid-template-columns: 1fr; } .gallery { grid-template-columns: 1fr; } .gallery-item:first-child { grid-column: span 1; grid-row: span 1; } .booking-card { position: relative; top: 0; } .amenities-grid { grid-template-columns: 1fr; } }</style></head><body><header><div class=\"container header-content\"><a href=\"#\" class=\"logo\">StayDirect</a><a href=\"#booking\" class=\"book-now-btn\">Book Now</a></div></header><main class=\"container\"><div class=\"gallery\">' + galleryHtml + '</div><div class=\"main-content\"><div class=\"listing-details\"><h1 class=\"listing-title\">' + title + '</h1><p class=\"listing-location\">' + locationParts + '</p><div class=\"listing-stats\"><span class=\"stat\">\ud83c\udfe0 ' + (data.maxGuests || 4) + ' guests</span><span class=\"stat\">\ud83d\udecf\ufe0f ' + (data.bedrooms || 1) + ' bedroom' + ((data.bedrooms || 1) > 1 ? 's' : '') + '</span><span class=\"stat\">\ud83d\udec1 ' + (data.bathrooms || 1) + ' bath' + ((data.bathrooms || 1) > 1 ? 's' : '') + '</span><span class=\"stat\">\ud83d\udecf\ufe0f ' + (data.beds || 1) + ' bed' + ((data.beds || 1) > 1 ? 's' : '') + '</span><span class=\"rating\"><span class=\"star\">\u2605</span><strong>' + (data.reviewScore?.overallRating || 4.8) + '</strong><span>(' + (data.reviewScore?.reviewsCount || 0) + ' reviews)</span></span></div><div class=\"host-section\">' + (data.hostProfile?.photo ? '<img src=\"' + data.hostProfile.photo + '\" alt=\"' + (data.hostProfile?.name || 'Your Host') + '\" class=\"host-photo\">' : '<div class=\"host-photo\"></div>') + '<div class=\"host-info\"><h3>Hosted by ' + (data.hostProfile?.name || 'Your Host') + '</h3>' + (data.hostProfile?.isSuperhost ? '<span class=\"superhost-badge\">\ud83c\udfc6 Superhost</span>' : '') + '</div></div><div class=\"section\"><h2 class=\"section-title\">About this place</h2><p class=\"description\">' + description + '</p></div><div class=\"section\"><h2 class=\"section-title\">What this place offers</h2><ul class=\"amenities-grid\">' + amenitiesHtml + '</ul></div><div class=\"section\"><h2 class=\"section-title\">House Rules</h2><ul class=\"rules-list\">' + houseRulesHtml + '</ul></div></div><div class=\"booking-sidebar\"><div class=\"booking-card\" id=\"booking\"><div class=\"price-line\"><span class=\"price\">' + currency + price + '</span><span class=\"price-period\">/ night</span></div><form class=\"booking-form\"><div class=\"form-row\"><div class=\"form-field\"><label>Check-in</label><input type=\"date\" name=\"checkin\" required></div><div class=\"form-field\"><label>Checkout</label><input type=\"date\" name=\"checkout\" required></div></div><div class=\"form-row\"><div class=\"form-field full-width\"><label>Guests</label><select name=\"guests\">' + Array.from({length: data.maxGuests || 4}, (_, i) => '<option value=\"' + (i+1) + '\">' + (i+1) + ' guest' + (i > 0 ? 's' : '') + '</option>').join('') + '</select></div></div></form><button type=\"submit\" class=\"reserve-btn\">Reserve</button><p class=\"booking-note\">You won\\'t be charged yet</p></div></div></div></main><footer><div class=\"container footer-content\"><p>\u00a9 2025 ' + title + '. All rights reserved.</p><p style=\"margin-top: 8px;\">Book directly and save on fees!</p><p style=\"margin-top: 16px;\">Powered by <a href=\"https://sitebuilder.shortrentals.ai/\" target=\"_blank\" style=\"color: var(--primary); text-decoration: none;\">shortrentals.ai</a></p></div></footer></body></html>';\n\nconst slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').substring(0, 30) || 'direct-booking';\nconst siteName = 'booking-' + slug + '-' + Date.now().toString(36);\n\nreturn { html, siteName, title, description: description.substring(0, 160) };"
      },
      "typeVersion": 2
    },
    {
      "id": "code-prepare-binary",
      "name": "Prepare Binary",
      "type": "n8n-nodes-base.code",
      "position": [
        -80,
        -48
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const html = $input.first().json.html;\nconst siteName = $input.first().json.siteName;\nconst title = $input.first().json.title;\n\nconst binaryData = await this.helpers.prepareBinaryData(\n  Buffer.from(html, 'utf-8'),\n  'index.html',\n  'text/html'\n);\n\nreturn {\n  json: { siteName, title, html },\n  binary: { data: binaryData }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "compression-zip",
      "name": "Create ZIP",
      "type": "n8n-nodes-base.compression",
      "position": [
        112,
        -48
      ],
      "parameters": {
        "fileName": "site.zip",
        "operation": "compress",
        "outputFormat": "zip",
        "binaryPropertyName": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "http-create-site",
      "name": "Create Netlify Site",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        304,
        -48
      ],
      "parameters": {
        "url": "https://api.netlify.com/api/v1/sites",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ name: $('Prepare Binary').item.json.siteName }) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "http-deploy-zip",
      "name": "Deploy ZIP",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        528,
        -48
      ],
      "parameters": {
        "url": "=https://api.netlify.com/api/v1/sites/{{ $json.site_id }}/builds",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/zip"
            }
          ]
        },
        "inputDataFieldName": "={{ $(\"Create ZIP\").item.binary.data }}"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "code-output-1",
      "name": "Output Results",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        -48
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const siteData = $('Create Netlify Site').item.json;\nconst deployData = $input.first().json;\nconst prepData = $('Prepare Binary').item.json;\n\nreturn {\n  success: true,\n  message: 'Direct booking site deployed successfully!',\n  title: prepData.title,\n  siteUrl: siteData.ssl_url || siteData.url,\n  adminUrl: siteData.admin_url,\n  siteId: siteData.site_id,\n  deployId: deployData.id,\n  deployState: deployData.state,\n  note: 'Your site is now live! Visit the siteUrl to see your direct booking page.'\n};"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "Create ZIP": {
      "main": [
        [
          {
            "node": "Create Netlify Site",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deploy ZIP": {
      "main": [
        [
          {
            "node": "Output Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airbnb Scraper": {
      "main": [
        [
          {
            "node": "Generate HTML Site",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Set Listing ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Binary": {
      "main": [
        [
          {
            "node": "Create ZIP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Listing ID": {
      "main": [
        [
          {
            "node": "Airbnb Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate HTML Site": {
      "main": [
        [
          {
            "node": "Prepare Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Netlify Site": {
      "main": [
        [
          {
            "node": "Deploy ZIP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}