{
  "nodes": [
    {
      "id": "ae0639dc-3064-4489-99ce-34c7ae49c1ed",
      "name": "\ud83d\udccb Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        -656
      ],
      "parameters": {
        "width": 620,
        "height": 588,
        "content": "\ud83d\udecd\ufe0f AI Product Catalog Architect\nThe Problem: Manual product entry\u2014uploading, resizing, and writing descriptions\u2014is a bottleneck for e-commerce growth.\nThe Solution: A mobile-to-store pipeline that hosts images via UploadToURL, generates SEO content via Vision AI, and creates listings on Shopify or WooCommerce.\n\n\u2699\ufe0f How it Works\nWebhook: Receives image (Binary or URL) + SKU/Price metadata.\n\nUploadToURL: Instantly hosts the image and returns a CDN link.\n\nGPT-4o Vision: Analyzes the photo to write titles, descriptions, and tags.\n\nStore Integration: Creates the product listing directly in your store.\n\n\ud83d\udd10 Credentials & Setup\nNode: Install n8n-nodes-uploadtourl (Community Node).\n\nAPIs: UploadToURL, OpenAI (GPT-4o), and Shopify/WooCommerce.\n\nVariable: Set DEFAULT_PLATFORM to shopify or woocommerce."
      },
      "typeVersion": 1
    },
    {
      "id": "e1482d72-5e54-4f41-ad4a-776593008d19",
      "name": "Entry & Validation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 576,
        "height": 578,
        "content": "## \ud83d\udeaa Entry & Validation\n**Nodes:** Webhook \u2192 Validate & Enrich \u2192 Has Remote URL?\n\n- Accepts `POST` with product metadata + either a `fileUrl` (remote) or binary file attachment\n- Validates required fields: needs at least `filename` or `fileUrl` plus a valid `platform` value\n- Sanitises SKU (uppercased, stripped of special chars), coerces price to float, defaults inventory to 1\n- Detects image MIME type from extension for correct Content-Type downstream\n- IF branch routes to the right upload path without duplicating logic"
      },
      "typeVersion": 1
    },
    {
      "id": "5d68e82d-7573-4d9c-9338-64f7d1371876",
      "name": "Upload + Vision AI",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 722,
        "content": "## \u2601\ufe0f Upload to URL \u2192 Vision AI\n**Nodes:** Upload to URL (\u00d72) \u2192 Extract URL \u2192 GPT-4o Vision \u2192 Parse AI Response\n\n- Native **Upload to URL** community node handles both URL-fetch and binary-upload paths \u2014 no custom HTTP node needed\n- `Extract URL` normalises the response shape across both paths\n- GPT-4o Vision receives the **actual image URL** and generates: product title, SEO meta description, 5 bullet features, suggested category, tags array, and a short marketing blurb\n- `Parse AI Response` validates and flattens the JSON, merges with original metadata, and computes `handle` (URL slug) from the AI title"
      },
      "typeVersion": 1
    },
    {
      "id": "114fad13-8471-41da-9db2-8af369336a5d",
      "name": "Platform Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2160,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 770,
        "content": "## \ud83c\udfea Platform Routing & Product Creation\n**Nodes:** Route by Platform \u2192 Shopify Create \u2192 WooCommerce Create\n\n- Switch node reads `platform` field (`shopify` or `woocommerce`) \u2014 extend with more outputs for other platforms\n- **Shopify branch:** creates product via GraphQL-compatible REST body with `images` array pre-set to the UploadToURL CDN link, sets `status: active` or `draft` based on `publishImmediately`\n- **WooCommerce branch:** creates product with `images[0].src` set to the CDN link, maps all AI-generated fields to WC schema including `short_description` and `tags`\n- Both branches pass `compare_at_price` / `regular_price` for sale display"
      },
      "typeVersion": 1
    },
    {
      "id": "b44b85fd-fe8f-4b18-b947-78d38570875c",
      "name": "Notification & Response",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2768,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 654,
        "content": "## \ud83d\udce3 Notification & Response\n**Nodes:** Build Response \u2192 Slack Notifier \u2192 Respond to Webhook\n\n- `Build Response` merges platform API reply with product metadata into a clean summary object\n- **Slack Notifier** posts a formatted message with product name, image thumbnail link, store URL, SKU, and price \u2014 easy to disable if not needed\n- Final webhook response returns `productId`, `productUrl`, `imageUrl`, `title`, `sku`, `platform` \u2014 ready to consume by a mobile app or Zapier downstream"
      },
      "typeVersion": 1
    },
    {
      "id": "016d11e3-a873-4796-806f-5d6f9b036420",
      "name": "Webhook - Receive Product",
      "type": "n8n-nodes-base.webhook",
      "notes": "Accepts POST with product metadata + fileUrl or binary image. Supports both mobile uploads and remote URL ingestion.",
      "position": [
        672,
        224
      ],
      "parameters": {
        "path": "product-catalog-upload",
        "options": {
          "allowedOrigins": "*"
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "14fe3281-67a8-4ce6-84c3-6fea8102a6e8",
      "name": "Validate & Enrich Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        224
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\n\n// --- Platform validation ---\nconst allowedPlatforms = ['shopify', 'woocommerce'];\nconst platform = (body.platform || $vars.DEFAULT_PLATFORM || 'shopify').toLowerCase();\nif (!allowedPlatforms.includes(platform)) {\n  throw new Error(`Invalid platform \"${platform}\". Must be: ${allowedPlatforms.join(' | ')}`);\n}\n\n// --- File source validation ---\nif (!body.fileUrl && !body.filename) {\n  throw new Error('Provide either fileUrl (remote image) or filename (for binary upload).');\n}\n\n// --- Filename & MIME detection ---\nconst filename = body.filename || body.fileUrl?.split('?')[0].split('/').pop() || 'product.jpg';\nconst ext = filename.split('.').pop()?.toLowerCase() || 'jpg';\nconst mimeMap = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp', gif: 'image/gif', avif: 'image/avif' };\nconst mimeType = mimeMap[ext] || 'image/jpeg';\n\n// --- SKU sanitisation ---\nconst rawSku = body.sku || '';\nconst sku = rawSku.toUpperCase().replace(/[^A-Z0-9\\-_]/g, '').trim() || `SKU-${Date.now()}`;\n\n// --- Price coercion ---\nconst price = parseFloat(body.price) || 0;\nconst compareAtPrice = parseFloat(body.compareAtPrice) || null;\nif (price < 0) throw new Error('Price cannot be negative.');\n\n// --- Inventory ---\nconst inventory = parseInt(body.inventory, 10);\nconst safeInventory = isNaN(inventory) ? 1 : Math.max(0, inventory);\n\n// --- Tags ---\nconst tagsRaw = body.tags || '';\nconst tagsArray = tagsRaw.split(',').map(t => t.trim()).filter(Boolean);\n\nreturn [{\n  json: {\n    // Source\n    filename,\n    fileUrl: body.fileUrl || null,\n    mimeType,\n    // Platform\n    platform,\n    publishImmediately: body.publishImmediately !== false,\n    // Product fields\n    sku,\n    price,\n    compareAtPrice,\n    vendor: body.vendor || '',\n    productType: body.productType || '',\n    inventory: safeInventory,\n    weight: parseFloat(body.weight) || null,\n    tags: tagsArray,\n    // Pass-through for AI context\n    userTitle: body.title || '',\n    userDescription: body.description || '',\n    // Meta\n    receivedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7a2b2887-718f-4d76-820a-c5b452e706c5",
      "name": "Has Remote URL?",
      "type": "n8n-nodes-base.if",
      "position": [
        1104,
        224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-fileurl",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.fileUrl }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a760c1eb-3636-4acf-854f-fd995a00c5b7",
      "name": "Upload to URL - Remote",
      "type": "n8n-nodes-uploadtourl.uploadToUrl",
      "position": [
        1328,
        112
      ],
      "parameters": {
        "operation": "uploadFile"
      },
      "credentials": {
        "uploadToUrlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1e27bcd3-29cb-41a6-acfb-ee74a10b6ef6",
      "name": "Upload to URL - Binary",
      "type": "n8n-nodes-uploadtourl.uploadToUrl",
      "position": [
        1328,
        352
      ],
      "parameters": {
        "operation": "uploadFile"
      },
      "credentials": {
        "uploadToUrlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4117a58c-304e-4ac7-b5e2-3b0fbcbfbe6c",
      "name": "Extract Image URL",
      "type": "n8n-nodes-base.code",
      "position": [
        1552,
        224
      ],
      "parameters": {
        "jsCode": "const uploadResp = $input.first().json;\nconst meta = $('Validate & Enrich Payload').first().json;\n\nconst imageUrl =\n  uploadResp.url ||\n  uploadResp.link ||\n  uploadResp.data?.url ||\n  uploadResp.file?.url ||\n  uploadResp.shortUrl;\n\nif (!imageUrl) {\n  throw new Error('Upload to URL returned no public URL. Raw response: ' + JSON.stringify(uploadResp));\n}\n\n// Ensure URL is HTTPS (some CDNs return HTTP)\nconst secureUrl = imageUrl.replace(/^http:\\/\\//, 'https://');\n\nreturn [{\n  json: {\n    ...meta,\n    imageUrl: secureUrl,\n    uploadId: uploadResp.id || uploadResp.data?.id || null,\n    fileSizeBytes: uploadResp.size || uploadResp.data?.size || null\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f0f9f546-2e0a-4ee1-a846-778aa59b4bc8",
      "name": "GPT-4o Vision - Analyse Product",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "notes": "Sends the uploaded image URL to GPT-4o Vision. Returns structured product content: title, SEO description, features, tags, category, and a URL handle.",
      "position": [
        1760,
        224
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "options": {
          "maxTokens": 1200,
          "temperature": 0.6
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are a professional e-commerce copywriter and product categorisation expert. Analyse the product image provided and return ONLY a valid JSON object \u2014 no markdown, no preamble, no trailing text."
            },
            {
              "content": "=Analyse this product image and generate all required e-commerce content.\n\nImage URL: {{ $json.imageUrl }}\nUser-provided title (may be empty): {{ $json.userTitle }}\nUser-provided description (may be empty): {{ $json.userDescription }}\nVendor: {{ $json.vendor || 'Unknown' }}\nProduct Type hint: {{ $json.productType || 'Not specified' }}\nExisting tags: {{ $json.tags.join(', ') || 'None' }}\nPrice: {{ $json.price }}\n\nReturn ONLY this JSON structure:\n{\n  \"title\": \"Short compelling product title (max 70 chars)\",\n  \"seoTitle\": \"SEO-optimised title with keywords (max 70 chars)\",\n  \"description\": \"Rich HTML product description (2-3 paragraphs, can use <p> <strong> <ul> <li> tags)\",\n  \"shortDescription\": \"One-sentence plain text summary for WooCommerce short_description\",\n  \"bulletFeatures\": [\"Feature 1\", \"Feature 2\", \"Feature 3\", \"Feature 4\", \"Feature 5\"],\n  \"suggestedCategory\": \"Most specific applicable product category\",\n  \"suggestedTags\": [\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"],\n  \"metaDescription\": \"SEO meta description (max 155 chars)\",\n  \"handle\": \"url-slug-from-title-lowercase-hyphenated\",\n  \"marketingBlurb\": \"One punchy sentence for social media or email marketing\",\n  \"confidenceScore\": 0.95\n}"
            }
          ]
        }
      },
      "typeVersion": 1.5
    },
    {
      "id": "14f53b63-c22a-4030-989b-46b51475926f",
      "name": "Parse & Merge AI Product Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        224
      ],
      "parameters": {
        "jsCode": "const aiRaw = $input.first().json;\nconst productData = $('Extract Image URL').first().json;\n\n// --- Parse AI response ---\nlet ai;\ntry {\n  const raw = aiRaw.message?.content || aiRaw.choices?.[0]?.message?.content || aiRaw.content || aiRaw.text;\n  ai = typeof raw === 'string' ? JSON.parse(raw) : raw;\n} catch (e) {\n  throw new Error('Failed to parse GPT-4o JSON response: ' + e.message);\n}\n\n// --- Validate essentials ---\nif (!ai.title) throw new Error('GPT-4o did not return a product title.');\n\n// --- Merge user-provided tags with AI tags (deduplicated) ---\nconst allTags = [...new Set([\n  ...productData.tags,\n  ...(ai.suggestedTags || [])\n])].slice(0, 15); // Most platforms cap at 15 tags\n\n// --- Build clean URL handle ---\nconst handle = (ai.handle || ai.title)\n  .toLowerCase()\n  .replace(/[^a-z0-9\\s-]/g, '')\n  .replace(/\\s+/g, '-')\n  .replace(/-+/g, '-')\n  .trim();\n\n// --- Shopify status ---\nconst shopifyStatus = productData.publishImmediately ? 'active' : 'draft';\n\n// --- WooCommerce status ---\nconst wcStatus = productData.publishImmediately ? 'publish' : 'draft';\n\nreturn [{\n  json: {\n    // Pass-through\n    platform: productData.platform,\n    imageUrl: productData.imageUrl,\n    sku: productData.sku,\n    price: productData.price,\n    compareAtPrice: productData.compareAtPrice,\n    vendor: productData.vendor,\n    productType: productData.productType || ai.suggestedCategory,\n    inventory: productData.inventory,\n    weight: productData.weight,\n    uploadId: productData.uploadId,\n\n    // AI-generated\n    title: productData.userTitle || ai.title,\n    seoTitle: ai.seoTitle,\n    description: ai.description,\n    shortDescription: ai.shortDescription,\n    bulletFeatures: ai.bulletFeatures || [],\n    category: ai.suggestedCategory,\n    tags: allTags,\n    metaDescription: ai.metaDescription,\n    handle,\n    marketingBlurb: ai.marketingBlurb,\n    confidenceScore: ai.confidenceScore || null,\n\n    // Platform-specific computed\n    shopifyStatus,\n    wcStatus,\n\n    generatedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "2d70dfc8-89e8-4ef2-ba5e-d603d1131d33",
      "name": "Route by Platform",
      "type": "n8n-nodes-base.switch",
      "position": [
        2208,
        224
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Shopify",
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.platform }}",
                    "rightValue": "shopify"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "WooCommerce",
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.platform }}",
                    "rightValue": "woocommerce"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "14303dea-d4f1-49ac-8278-97d285f669b2",
      "name": "Shopify - Create Product",
      "type": "n8n-nodes-base.shopify",
      "notes": "Creates product with image URL pre-attached. Sets SEO metafields, variant pricing, and inventory. Status: active or draft based on publishImmediately.",
      "position": [
        2432,
        112
      ],
      "parameters": {
        "title": "={{ $json.title }}",
        "resource": "product",
        "authentication": "oAuth2",
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "866f00db-805f-4884-b5ec-541651235f0a",
      "name": "WooCommerce - Create Product",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Creates WooCommerce product via REST API. Sets image src to UploadToURL CDN link. Writes Yoast SEO meta fields and marketing blurb to post meta.",
      "position": [
        2432,
        352
      ],
      "parameters": {
        "url": "={{ $credentials.url }}/wp-json/wc/v3/products",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={\n  \"name\": {{ JSON.stringify($json.title) }},\n  \"slug\": {{ JSON.stringify($json.handle) }},\n  \"type\": \"simple\",\n  \"status\": {{ JSON.stringify($json.wcStatus) }},\n  \"description\": {{ JSON.stringify($json.description) }},\n  \"short_description\": {{ JSON.stringify($json.shortDescription) }},\n  \"sku\": {{ JSON.stringify($json.sku) }},\n  \"regular_price\": {{ JSON.stringify(String($json.price)) }},\n  \"sale_price\": {{ $json.compareAtPrice ? JSON.stringify(String($json.price)) : '\"\"' }},\n  \"manage_stock\": true,\n  \"stock_quantity\": {{ $json.inventory }},\n  \"weight\": {{ JSON.stringify($json.weight ? String($json.weight) : '') }},\n  \"categories\": [{ \"name\": {{ JSON.stringify($json.category) }} }],\n  \"tags\": {{ JSON.stringify($json.tags.map(t => ({ name: t }))) }},\n  \"images\": [{ \"src\": {{ JSON.stringify($json.imageUrl) }}, \"alt\": {{ JSON.stringify($json.title) }}, \"position\": 1 }],\n  \"meta_data\": [\n    { \"key\": \"_yoast_wpseo_title\", \"value\": {{ JSON.stringify($json.seoTitle) }} },\n    { \"key\": \"_yoast_wpseo_metadesc\", \"value\": {{ JSON.stringify($json.metaDescription) }} },\n    { \"key\": \"marketing_blurb\", \"value\": {{ JSON.stringify($json.marketingBlurb) }} }\n  ]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "16ff0092-fdda-4675-aae9-594a12578d9d",
      "name": "Build Product Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2640,
        224
      ],
      "parameters": {
        "jsCode": "const platformResp = $input.first().json;\nconst productData = $('Parse & Merge AI Product Data').first().json;\n\n// Normalise Shopify vs WooCommerce response shapes\nconst isShopify = productData.platform === 'shopify';\n\nconst productId = isShopify\n  ? (platformResp.product?.id || platformResp.id)\n  : (platformResp.id);\n\nconst productUrl = isShopify\n  ? `https://${platformResp.product?.handle || productData.handle}.myshopify.com/products/${platformResp.product?.handle || productData.handle}`\n  : (platformResp.permalink || `${platformResp.link || ''}`);\n\nconst imageUrl = isShopify\n  ? (platformResp.product?.images?.[0]?.src || productData.imageUrl)\n  : (platformResp.images?.[0]?.src || productData.imageUrl);\n\nif (!productId) {\n  throw new Error(`${productData.platform} did not return a product ID. Response: ${JSON.stringify(platformResp).slice(0, 300)}`);\n}\n\nreturn [{\n  json: {\n    success: true,\n    platform: productData.platform,\n    productId: String(productId),\n    productUrl,\n    imageUrl,\n    title: productData.title,\n    sku: productData.sku,\n    price: productData.price,\n    compareAtPrice: productData.compareAtPrice,\n    category: productData.category,\n    tags: productData.tags,\n    handle: productData.handle,\n    inventory: productData.inventory,\n    marketingBlurb: productData.marketingBlurb,\n    confidenceScore: productData.confidenceScore,\n    publishStatus: productData.shopifyStatus || productData.wcStatus,\n    generatedAt: productData.generatedAt,\n    createdAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "b247362f-8b43-4487-9055-714a9f21f04c",
      "name": "Slack - Notify Team",
      "type": "n8n-nodes-base.slack",
      "notes": "Optional: posts a formatted Slack notification to #product-updates with image link, product URL, SKU, price, and AI confidence score. Disable this node if Slack is not used.",
      "position": [
        2864,
        112
      ],
      "parameters": {
        "text": "=:new: *New product created!*\n\n*{{ $json.title }}*\nSKU: `{{ $json.sku }}` | Price: ${{ $json.price }}{{ $json.compareAtPrice ? ' ~~$' + $json.compareAtPrice + '~~' : '' }}\nPlatform: {{ $json.platform | upcase }}\nCategory: {{ $json.category }}\nTags: {{ $json.tags.join(', ') }}\n\n:frame_with_picture: <{{ $json.imageUrl }}|View image>\n:link: <{{ $json.productUrl }}|View product>\n\n_AI confidence: {{ Math.round(($json.confidenceScore || 0) * 100) }}%_\n_{{ $json.marketingBlurb }}_",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2cbafe11-18db-4f5d-a02a-5c2b7c16376e",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2864,
        320
      ],
      "parameters": {
        "options": {
          "responseCode": 201,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ $('Build Product Response').first().json }}"
      },
      "typeVersion": 1.1
    }
  ],
  "connections": {
    "Has Remote URL?": {
      "main": [
        [
          {
            "node": "Upload to URL - Remote",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Upload to URL - Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Image URL": {
      "main": [
        [
          {
            "node": "GPT-4o Vision - Analyse Product",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Platform": {
      "main": [
        [
          {
            "node": "Shopify - Create Product",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "WooCommerce - Create Product",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack - Notify Team": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Product Response": {
      "main": [
        [
          {
            "node": "Slack - Notify Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to URL - Binary": {
      "main": [
        [
          {
            "node": "Extract Image URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to URL - Remote": {
      "main": [
        [
          {
            "node": "Extract Image URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shopify - Create Product": {
      "main": [
        [
          {
            "node": "Build Product Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate & Enrich Payload": {
      "main": [
        [
          {
            "node": "Has Remote URL?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Product": {
      "main": [
        [
          {
            "node": "Validate & Enrich Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WooCommerce - Create Product": {
      "main": [
        [
          {
            "node": "Build Product Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse & Merge AI Product Data": {
      "main": [
        [
          {
            "node": "Route by Platform",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT-4o Vision - Analyse Product": {
      "main": [
        [
          {
            "node": "Parse & Merge AI Product Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}