AutomationFlowsAI & RAG › Baselinker Product Automation - Full Sanitized Workflow

Baselinker Product Automation - Full Sanitized Workflow

BaseLinker Product Automation - Full Sanitized Workflow. Uses googleGemini, editImage, httpRequest, discord. Webhook trigger; 89 nodes.

Webhook trigger★★★★★ complexityAI-powered89 nodesGoogle GeminiEdit ImageHTTP RequestDiscord
AI & RAG Trigger: Webhook Nodes: 89 Complexity: ★★★★★ AI nodes: yes Added:

This workflow follows the Discord → HTTP Request recipe pattern — see all workflows that pair these two integrations.

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 →

Download .json
{
  "name": "BaseLinker Product Automation - Full Sanitized Workflow",
  "nodes": [
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "7ca45a22-c386-4f9f-98e4-9a85fe1206a3",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.stareSzablony }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -1552,
        3344
      ],
      "id": "cf6ec122-6300-4457-8bf3-117f83f9e6ae",
      "name": "Stare szablony"
    },
    {
      "parameters": {
        "jsCode": "const state = { ...$input.item.json };\n\n// 1. Sprawd\u017a, czy obraz jest ju\u017c przekazany z poprzedniego w\u0119z\u0142a\nif ($input.item.binary && $input.item.binary.image) {\n  return { json: state, binary: { image: $input.item.binary.image } };\n}\n\n// 2. Pobierz dane binarne z Webhooka\nconst wb = $('Webhook - Odbierz Produkty').first().binary || {};\n\n// 3. Pobierz list\u0119 plik\u00f3w przygotowan\u0105 i posortowan\u0105 przez Tw\u00f3j w\u0119ze\u0142 \"Parsuj Nazwy Plik\u00f3w\"\nconst parsedData = $('Parsuj Nazwy Plik\u00f3w1').first().json;\nconst regularEntries = parsedData.regularEntries || [];\n\nif (regularEntries.length === 0) {\n  throw new Error('[B\u0141\u0104D] Brak zwyk\u0142ych zdj\u0119\u0107. Nie mo\u017cna pobra\u0107 ostatniego obrazu.');\n}\n\n// 4. \"Parsuj Nazwy Plik\u00f3w\" sortuje pliki rosn\u0105co, wi\u0119c ostatni na li\u015bcie to obraz z najwy\u017cszym numerem\nconst lastEntry = regularEntries[regularEntries.length - 1];\nconst lastKey = lastEntry.key;\n\n// 5. Zwr\u00f3\u0107 plik binarny, kt\u00f3ry odpowiada temu ostatniemu kluczowi\nif (!wb[lastKey]) {\n  throw new Error(`[B\u0141\u0104D] Nie znaleziono danych binarnych dla klucza: ${lastKey}`);\n}\n\nreturn { \n  json: state, \n  binary: { \n    image: wb[lastKey] \n  } \n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1504,
        2416
      ],
      "id": "e077d1a7-1199-4da9-9bb3-12ea75bdb32b",
      "name": "Wstrzyknij Ostatni obraz"
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "analyze",
        "modelId": {
          "__rl": true,
          "value": "models/gemini-2.5-flash-lite",
          "mode": "list"
        },
        "text": "=Jeste\u015b ekspertem od gramatyki polskiej i opis\u00f3w produkt\u00f3w.\n\n{{ $json.descAttempt > 0 ? '\u26a0\ufe0f POPRZEDNIA PR\u00d3BA NR ' + $json.descAttempt + ' ODRZUCONA\\nOdrzucone: \"' + $json.currentDesc + '\"\\nPow\u00f3d: ' + $json.lastDescReason + ($json.lastDescSuggestion ? '\\nSugestia: ' + $json.lastDescSuggestion : '') + '\\n\\nUWZGL\u0118DNIJ uwagi i wygeneruj POPRAWIONY TEKST.\\n' : '' }}\n\nZADANIE: Na podstawie OBRAZU oraz SZABLONU wygeneruj PE\u0141NY szablon odpowiednio dostosowany.\n\nSZABLON:\n{{ $('Inicjalizuj Stan1').item.json.szablon }}\n\nTYTU\u0141 PRODUKTU (TYLKO KONTEKST \u2014 NIE WOLNO GO PRZEPISYWA\u0106 ANI MODYFIKOWA\u0106):\n{{ $('Parsuj Wer T1').item.json.currentTitle }}\n\nINSTRUKCJA:\n1. Zidentyfikuj g\u0142\u00f3wny motyw widoczny na obrazie\n2. Dostosuj opis pod ten konkretny motyw przytoczony w tytule oraz widoczny na zdjeciu\n3. Zwr\u00f3\u0107 CA\u0141Y OPIS w takiej samej postaci jak\u0105 dosta\u0142es ale odpowiednie dostosowany\n4. Dopasuj poprawnie gramatyk\u0119 (przypadek, liczba, rodzaj)\n5. Popraw u\u017cycie \u201ez\u201d / \u201eze\u201d (np. \u201eze zwierz\u0119tami\u201d, \u201ez kotem\u201d, \u201ez motywem\u201d, \"z liskami\", \"z gwiazdami\")\n\nZASADY KRYTYCZNE:\n- ZWRACASZ CA\u0141Y OPIS\n- NIE zwracasz samego fragmentu\n- NIE przepisujesz ani nie modyfikujesz tytu\u0142u\n- NIE dodajesz \u017cadnych dodatkowych zda\u0144 ani komentarzy\n- NIE u\u017cywasz cudzys\u0142ow\u00f3w\n- PAMI\u0118TAJ BARDZO WA\u017bNE by zwr\u00f3ci\u0107 DOK\u0141ADNIE CA\u0141Y opis czyli ze wszystkimi znacznikami html zamykaj\u0105cymi otwieraj\u0105cymi itp\n- D\u0141UGO\u015a\u0106 ZWR\u00d3CONEJ ODPOWIEDZI powinna by\u0107 bardzo zbli\u017cona do szablonu A NAPEWNO NIE UCI\u0118TA W PO\u0141OWIE DAJ *CA\u0141O\u015a\u0106*\n\nPRZYK\u0141AD:\nSzablon: <section class=\"\\&quot;section\\&quot;\"><div class=\"\\&quot;item\" item-12\\\"=\"\"><section class=\"\\&quot;text-item\\&quot;\"><p>Zmie\u0144 pok\u00f3j swojego malucha w prehistoryczn\u0105 krain\u0119....\nPoprawna odpowied\u017a: <section class=\"\\&quot;section\\&quot;\"><div class=\"\\&quot;item\" item-12\\\"=\"\"><section class=\"\\&quot;text-item\\&quot;\"><p>Zamie\u0144 pok\u00f3j dziecka w prawdziwe centrum kosmiczne....\nODPOWIED\u0179:\nZwr\u00f3\u0107 WY\u0141\u0104CZNIE gotowy opis",
        "inputType": "binary",
        "binaryPropertyName": "image",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        -1088,
        2416
      ],
      "id": "b9999563-6cc6-4272-b471-430e66c92b20",
      "name": "Generuj Opis1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "99d5a23d-2ca1-4d78-b138-3ad2b0751a94",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.stareSzablony }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {
          "ignoreCase": false
        }
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -4736,
        3424
      ],
      "id": "31736d36-9cd9-4ee3-ae24-23e82fece075",
      "name": "If3"
    },
    {
      "parameters": {
        "jsCode": "const products = $('2. Pobierz pe\u0142ne dane3').first().json.products;\nconst firstKey = Object.keys(products || {})[0];\nif (!firstKey || !products[firstKey]) throw new Error('[B\u0141\u0104D] Brak produkt\u00f3w.');\nconst description = products[firstKey].text_fields?.description;\nif (!description) throw new Error('[B\u0141\u0104D] Brak pola description.');\nreturn [{ json: { result: description } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4432,
        3344
      ],
      "id": "cede9a1d-ff40-4ea1-a4f8-184242be296c",
      "name": "Pobierz Szablon Opisu1"
    },
    {
      "parameters": {
        "jsCode": "let aiResponse = $input.first().json.content?.parts?.[0]?.text || $input.first().json.text || \"\";\n\nconst szablon = $('Inicjalizuj Stan1').first().json.szablon || \"\";\n\n// Zamiast szuka\u0107 w nadpisanym JSON-ie (kt\u00f3ry Gemini czy\u015bci), \n// u\u017cywamy wbudowanej zmiennej $runIndex, kt\u00f3ra zlicza ile razy ten w\u0119ze\u0142 wykona\u0142 si\u0119 w p\u0119tli.\nlet currentAttempt = typeof $runIndex !== 'undefined' ? $runIndex : 0;\n\nlet cleanDesc = aiResponse.replace(/^```(html)?\\s*/gi, '').replace(/\\s*```$/g, '').trim();\n\nlet isValid = true;\nlet reason = \"\";\nlet suggestion = \"\";\n\nif (!cleanDesc) {\n    isValid = false;\n    reason = \"Odpowied\u017a AI jest ca\u0142kowicie pusta.\";\n    suggestion = \"Zwr\u00f3\u0107 pe\u0142ny kod HTML z opisem.\";\n}\n\nif (isValid && !cleanDesc.startsWith('<')) {\n    isValid = false;\n    reason = \"Wygenerowany tekst nie zaczyna si\u0119 od znacznika HTML (np. <section>). AI prawdopodobnie doda\u0142o wst\u0119p (np. 'Oto opis:').\";\n    suggestion = \"Zwr\u00f3\u0107 WY\u0141\u0104CZNIE kod HTML. Nie pisz \u017cadnych s\u0142\u00f3w wst\u0119pu, zacznij od znaku '<'.\";\n}\n\nif (isValid && cleanDesc.length < (szablon.length * 0.5)) {\n    isValid = false;\n    reason = `Zwr\u00f3cony opis jest nienaturalnie kr\u00f3tki (${cleanDesc.length} znak\u00f3w vs szablon ${szablon.length} znak\u00f3w). Zosta\u0142 brutalnie uci\u0119ty.`;\n    suggestion = \"Wygenerowa\u0142e\u015b tylko fragment. Przeanalizuj CA\u0141Y szablon i zwr\u00f3\u0107 go w 100% (od pocz\u0105tku do samego ko\u0144ca), bez ucinania w po\u0142owie.\";\n}\n\nconst countOpenSection = (cleanDesc.match(/<section/gi) || []).length;\nconst countCloseSection = (cleanDesc.match(/<\\/section>/gi) || []).length;\nconst countOpenDiv = (cleanDesc.match(/<div/gi) || []).length;\nconst countCloseDiv = (cleanDesc.match(/<\\/div>/gi) || []).length;\n\nif (isValid && (countOpenSection !== countCloseSection || countOpenDiv !== countCloseDiv)) {\n    isValid = false;\n    reason = `B\u0142\u0105d sk\u0142adni HTML. Niezgodna liczba tag\u00f3w. <section> (otwarte: ${countOpenSection}, zamkni\u0119te: ${countCloseSection}), <div> (otwarte: ${countOpenDiv}, zamkni\u0119te: ${countCloseDiv}).`;\n    suggestion = \"Upewnij si\u0119, \u017ce KA\u017bDY otwarty tag <section> i <div> ma sw\u00f3j odpowiednik zamykaj\u0105cy (</section>, </div>). Skup si\u0119 na poprawnym zamkni\u0119ciu struktury HTML na samym ko\u0144cu, kod nie mo\u017ce by\u0107 urwany.\";\n}\n\nreturn {\n    json: {\n        ...$input.first().json,\n        isValid: isValid,\n        currentDesc: cleanDesc,\n        lastDescReason: reason,\n        lastDescSuggestion: suggestion,\n        descAttempt: isValid ? currentAttempt : currentAttempt + 1\n    }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -832,
        2416
      ],
      "id": "e079f6d7-190f-4077-9a5e-58ef148672af",
      "name": "Weryfikator Opisu"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "570dd3d7-a258-4af7-a17a-f074014565b8",
              "leftValue": "={{ $json.isValid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            },
            {
              "id": "b1be06e6-aa58-4b2b-8e75-06054307cdf1",
              "leftValue": "={{ $json.descAttempt }}",
              "rightValue": 2,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -624,
        2416
      ],
      "id": "9a980feb-4ec7-41cc-a1db-77f860b062e3",
      "name": "If6"
    },
    {
      "parameters": {
        "operation": "resize",
        "dataPropertyName": "=image",
        "width": 400,
        "height": 400,
        "resizeOption": "onlyIfLarger",
        "options": {}
      },
      "type": "n8n-nodes-base.editImage",
      "typeVersion": 1,
      "position": [
        -1296,
        2416
      ],
      "id": "c74e6647-b38a-4545-a064-eaa80b7f83c5",
      "name": "Zmie\u0144 Rozmiar1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "fb1a8c99-11f6-4d39-80ba-46938ead814f",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.stareSzablony }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -2000,
        4176
      ],
      "id": "ac3dc711-d621-4c3f-a4f1-8000565bc03d",
      "name": "If7"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nif (data.payload && data.payload.text_fields) {\n    data.payload.text_fields.description = $('mapuj1').first().json.description;\n}\n\nreturn [{ json: data }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1616,
        4032
      ],
      "id": "624c86e9-ebef-4e43-a9e7-a37ea403d608",
      "name": "Dostosuj Opis (AI ON & FULL Description)"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "16754619-392a-4619-9696-9ee4cd05aad9",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.stareSzablony }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -4832,
        4752
      ],
      "id": "dfcbf4b2-af5e-4020-be41-2f1129afe7e5",
      "name": "If8"
    },
    {
      "parameters": {
        "jsCode": "const products = $input.first().json.products || {};\nconst firstProduct = Object.values(products)[0] || {};\nconst description = firstProduct.text_fields?.description || \"\";\n\nreturn [\n  {\n    json: {\n      description: description\n    }\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4592,
        4672
      ],
      "id": "72826a87-4b2a-4a77-af80-d1ca3c486296",
      "name": "Przygotuj JSON dla Webhooka"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        -4400,
        4672
      ],
      "id": "217cd4c5-f6b8-45a7-b91e-8aba9b501bd6",
      "name": "Respond to Webhook4"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "fb1a8c99-11f6-4d39-80ba-46938ead814f",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.stareSzablony }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -1824,
        4432
      ],
      "id": "584b181c-e8b6-4e45-be35-e5f1cfff9c62",
      "name": "If9"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nif (data.payload && data.payload.text_fields) {\n    data.payload.text_fields.description = $('Webhook - Odbierz Produkty').first().json.body.semanticVariation;\n}\n\nreturn [{ json: data }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1664,
        4368
      ],
      "id": "4d9c398e-b58d-4b76-9b70-eb369f964f48",
      "name": "Dostosuj Opis (AI OFF & FULL Description)"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"description\": \"{{ $json.result }}\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        -4400,
        4816
      ],
      "id": "db71fb49-de2b-40e2-8ede-63ae492ab1a4",
      "name": "Respond to Webhook3"
    },
    {
      "parameters": {
        "jsCode": "const products=$input.first().json.products;\nconst firstKey=Object.keys(products)[0];\nif(!firstKey||!products[firstKey])throw new Error('Brak produkt\u00f3w');\nconst description=products[firstKey].text_fields?.description;\nif(!description)throw new Error('Brak description');\nconst match=description.match(/<b>(.*?)<\\/b>/i);\nif(!match)return[{json:{result:null,message:'Brak <b> w opisie'}}];\nlet text=match[1].trim().replace(/&nbsp;/gi,' ').replace(/\\s+/g,' ');\nconst words=text.split(/\\s+/);\nconst shortWords=['z','w','na','do','i','o','u'];\nlet cutIndex=words.length-2;\nif(shortWords.includes(words[words.length-2]?.toLowerCase()))cutIndex=words.length-1;\nif(words.length>2)text=words.slice(0,cutIndex).join(' ')+' ...';\nelse text='...';\nreturn[{json:{result:`<b>${text}</b>`}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4608,
        4816
      ],
      "id": "1304ae38-8652-4041-aed0-8620d3c5ead9",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventoryProductsData"
            },
            {
              "name": "parameters",
              "value": "={\"inventory_id\": {{ $('Pobierz ID Katalog\u00f3w1').item.json.inventories[0].inventory_id }},\"products\":[{{ Object.keys($json.products)[0] }}]}"
            }
          ]
        },
        "options": {}
      },
      "name": "2. Pobierz pe\u0142ne dane1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5040,
        4752
      ],
      "id": "987f9261-ca8b-4137-94d7-59a439dd4690"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "sku1-check",
              "leftValue": "={{ $json.products }}",
              "rightValue": "",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -5296,
        4752
      ],
      "id": "d1f4e02d-c83d-4ac5-86ea-7f9a7f0dec7e",
      "name": "Czy znaleziono sku2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventoryProductsList"
            },
            {
              "name": "parameters",
              "value": "={\"inventory_id\": {{ $json.inventories[0].inventory_id }},\"filter_sku\": \"{{ $('Webhook - Odbierz Produkty').item.json.body.sku }}\"}"
            }
          ]
        },
        "options": {}
      },
      "name": "1. Znajd\u017a po SKU1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5472,
        4752
      ],
      "id": "242991ab-a7a4-4e1d-852f-02719f8cdb67"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventories"
            }
          ]
        },
        "options": {}
      },
      "name": "Pobierz ID Katalog\u00f3w1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5664,
        4752
      ],
      "id": "035712a8-12b6-45c0-83ca-3af8dc1403a4"
    },
    {
      "parameters": {
        "respondWith": "noData",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        -1168,
        4352
      ],
      "id": "6c02bc48-6740-4228-8650-48ea01393220",
      "name": "Respond to Webhook5"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "addInventoryProduct"
            },
            {
              "name": "parameters",
              "value": "={{ JSON.stringify($json.payload) }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -1376,
        4352
      ],
      "id": "7d4e504c-62bc-4518-b5c0-4cffe3c571c4",
      "name": "8. Wy\u015blij do BaseLinker"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst desc = data.payload?.text_fields?.description;\n\nif (!desc) return [{ json: data }];\n\nconst m = desc.match(/<b>(.*?)<\\/b>/i);\nif (!m) return [{ json: data }];\n\nconst words = m[1].trim().split(/\\s+/);\n\nif (words.length > 0) {\n  words[words.length - 1] = $('Webhook - Odbierz Produkty').first().json.body.semanticVariation;\n}\n\n// \ud83d\udc47 dodana spacja przed </b>\ndata.payload.text_fields.description = desc.replace(\n  /<b>.*?<\\/b>/i,\n  `<b>${words.join(' ')} </b>`\n);\n\nreturn [{ json: data }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1664,
        4528
      ],
      "id": "7826e791-7854-4197-820c-194a2e999920",
      "name": "Dostosuj Opis (AI OFF)1"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst desc = data.payload?.text_fields?.description;\n\nif (!desc) return [{ json: data }];\n\nconst m = desc.match(/<b>(.*?)<\\/b>/i);\nif (!m) return [{ json: data }];\n\nconst origText = m[1].trim();\nconst mapText = $('mapuj1').first().json.description?.trim();\n\nif (!mapText) return [{ json: data }];\n\nconst origWords = origText.split(/\\s+/);\nconst firstWord = origWords[0].toLowerCase();\n\nlet newBContent = \"\";\n\n// 1. Sprawdzamy, czy w mapowaniu jest ju\u017c g\u0142\u00f3wne s\u0142owo z orygina\u0142u (np. \"Naklejki\")\nif (mapText.toLowerCase().includes(firstWord)) {\n    // Je\u015bli tak, mapowanie ma pe\u0142ny tekst - nadpisujemy ca\u0142o\u015b\u0107, by nie duplikowa\u0107\n    newBContent = mapText;\n} else {\n    // Je\u015bli nie, zachowujemy oryginalny pocz\u0105tek i podmieniamy tylko ostatnie s\u0142owo\n    origWords[origWords.length - 1] = mapText;\n    newBContent = origWords.join(' ');\n}\n\n// 2. Automatyczna korekta gramatyczna przedimk\u00f3w \"z\" / \"ze\"\n// Zmienia \"z s\u0142oniem\" na \"ze s\u0142oniem\", \"z zwierz\u0105tkami\" na \"ze zwierz\u0105tkami\"\nnewBContent = newBContent.replace(/\\b(z)\\s+(s\u0142|zw|z\u0142|sz|rz|zm|zn|zb|zd|zg|sk|sp|st|\u015bn)/gi, 'ze $2');\n// Zmienia b\u0142\u0119dne \"ze liskami\" na \"z liskami\", \"ze lwem\" na \"z lwem\"\nnewBContent = newBContent.replace(/\\b(ze)\\s+([lkmntpwjcbdgf])/gi, 'z $2');\n\n// 3. Wstrzykni\u0119cie poprawionego tekstu do g\u0142\u00f3wnego opisu\ndata.payload.text_fields.description = desc.replace(/<b>.*?<\\/b>/i, `<b>${newBContent}</b>`);\n\nreturn [{ json: data }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1616,
        4224
      ],
      "id": "66f86207-5eea-422e-bd32-5e77a86347e1",
      "name": "Dostosuj Opis (AI ON)1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "ai-check2",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.useAI }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -2144,
        4352
      ],
      "id": "8d8b2eab-feb3-4067-b1a6-7cf45590e3d7",
      "name": "If10"
    },
    {
      "parameters": {
        "mode": "chooseBranch",
        "useDataOfInput": 2
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -2336,
        4368
      ],
      "id": "1dd707d9-19f2-46a3-bd65-efafba75cb67",
      "name": "Merge1"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item;\nconst src  = $('4. Rozwi\u0144 na Osobne Zdj\u0119cia').item.json;\nconst base64 = item.json?.data || '';\nconst images = {};\n\nif (base64) {\n  // Znajd\u017a faktyczn\u0105 pozycj\u0119 w dedupedKeys po indeksie (0-based)\n  const orderedKeys = src.orderedKeys || [];\n  const keyIndex = orderedKeys.findIndex(e => e.key === src.currentKey);\n  const position = keyIndex >= 0 ? keyIndex : (src.currentPosition ?? 1) - 1;\n  images[String(position)] = `data:${base64}`;\n}\n\nreturn {\n  json: {\n    product: src.product,\n    orderedKeys: src.orderedKeys,\n    currentKey: src.currentKey,\n    currentPosition: src.currentPosition ?? 0,\n    totalImages: src.totalImages,\n    images,\n    skuData: src.skuData\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2800,
        4464
      ],
      "id": "48bc7751-8c27-42eb-a76c-c98046953efb",
      "name": "6. Przypisz Zdj\u0119cie do Pozycji"
    },
    {
      "parameters": {
        "jsCode": "const { product, orderedKeys, skuData } = $input.item.json;\nconst webhookBinary = $('Webhook - Odbierz Produkty').item.binary || {};\n\nif (!orderedKeys || orderedKeys.length === 0) {\n  return [{ json: { product, orderedKeys: [], currentKey: null, currentPosition: 1, totalImages: 0, images: {}, skuData } }];\n}\n\n// Odfiltruj duplikaty po fileName \u2014 zachowaj pierwsze wyst\u0105pienie\nconst seenFileNames = new Set();\nconst dedupedKeys = orderedKeys.filter(({ key }) => {\n  const fileName = webhookBinary[key]?.fileName;\n  if (!fileName || seenFileNames.has(fileName)) return false;\n  seenFileNames.add(fileName);\n  return true;\n});\n\nreturn dedupedKeys.map(({ key, position }) => ({\n  json: {\n    product,\n    orderedKeys: dedupedKeys,\n    currentKey: key,\n    currentPosition: position,\n    totalImages: dedupedKeys.length,\n    images: {},\n    skuData\n  },\n  binary: webhookBinary[key] ? { currentBinary: webhookBinary[key] } : {}\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -3296,
        4464
      ],
      "id": "f5509f86-de30-41f3-9bdd-65c58213b47e",
      "name": "4. Rozwi\u0144 na Osobne Zdj\u0119cia"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// 3. WYCI\u0104GNIJ DANE I KLUCZE ZDJ\u0118\u0106\n//\n// Miniaturka (min) \u2192 pozycja 1 w BaseLinker\n// Pozosta\u0142e zdj\u0119cia \u2192 pozycje 2, 3, 4... wg kolejno\u015bci numeru z nazwy pliku\n//\n// SKU: przekazywane z w\u0119z\u0142a 'Wybierz Miniaturk\u0119'\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nconst raw = $input.item.json;\nconst inventoryId = $('Pobierz ID Katalog\u00f3w3').item.json.inventories[0].inventory_id;\n\nconst productId = Object.keys(raw.products)[0];\nif (!productId) throw new Error('[VALIDATION] Brak produktu w odpowiedzi z BaseLinker.');\n\nconst product = raw.products[productId];\nproduct.inventory_id = String(inventoryId);\n\nconst webhookBinary = $('Webhook - Odbierz Produkty').item.binary || {};\n\nfunction parseFileName(fileName) {\n  if (!fileName) return null;\n  const base = fileName.replace(/\\.[^.]+$/, '');\n  const m = base.match(/^([A-Za-z]+-)?([0-9]+)_([^_]+)/);\n  if (!m) return null;\n  const prefix    = m[1] || '';\n  const skuRaw    = m[2];\n  const segment   = m[3].toLowerCase();\n  const isThumbnail = segment === 'min';\n  const imageNumber = isThumbnail ? null : parseInt(segment, 10);\n  return { prefix, skuRaw, isThumbnail, imageNumber };\n}\n\nconst allKeys = Object.keys(webhookBinary).filter(k => /^images\\d+$/.test(k));\nconst thumbnailRaw = [];\nconst regularRaw   = [];\n\nfor (const key of allKeys) {\n  const fileName = webhookBinary[key]?.fileName || '';\n  const parsed = parseFileName(fileName);\n  if (!parsed) continue;\n  if (parsed.isThumbnail) thumbnailRaw.push({ key, parsed, fileName });\n  else                    regularRaw.push({ key, parsed, fileName });\n}\n\nregularRaw.sort((a, b) => (a.parsed.imageNumber || 0) - (b.parsed.imageNumber || 0));\n\n// Miniaturka \u2014 klucz z w\u0119z\u0142a 'Wybierz Miniaturk\u0119' (je\u015bli dost\u0119pny)\nlet thumbKey;\ntry { thumbKey = $('Wybierz Miniaturk\u01191').first().json.chosenThumbnailKey; } catch(e) { thumbKey = thumbnailRaw[0]?.key; }\n\nconst orderedKeys = [];\nif (thumbKey && webhookBinary[thumbKey]) orderedKeys.push({ key: thumbKey, position: 1 });\n\nlet pos = 2;\nfor (const reg of regularRaw) {\n  if (webhookBinary[reg.key]) orderedKeys.push({ key: reg.key, position: pos++ });\n}\n\n// SKU data z w\u0119z\u0142a parsowania\nlet skuData = {};\ntry {\n  const pd = $('Wybierz Miniaturk\u01191').first().json;\n  skuData = { skuPrefix: pd.skuPrefix, skuRaw: pd.skuRaw };\n} catch(e) {\n  if (thumbnailRaw.length > 0) skuData = { skuPrefix: thumbnailRaw[0].parsed.prefix, skuRaw: thumbnailRaw[0].parsed.skuRaw };\n}\n\nreturn { json: { product, orderedKeys, skuData } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -3520,
        4464
      ],
      "id": "cf4dae0c-e9a5-46d2-a0de-eab6686afb9f",
      "name": "3. Wyci\u0105gnij Dane i Klucze Zdj\u0119\u0107"
    },
    {
      "parameters": {
        "jsCode": "const products = $('2. Pobierz pe\u0142ne dane3').first().json.products;\nconst firstKey = Object.keys(products || {})[0];\nif (!firstKey || !products[firstKey]) throw new Error('[B\u0141\u0104D] Brak produkt\u00f3w.');\nconst description = products[firstKey].text_fields?.description;\nif (!description) throw new Error('[B\u0141\u0104D] Brak pola description.');\nconst match = description.match(/<b>(.*?)<\\/b>/i);\nif (!match) return [{ json: { result: null, message: 'Brak tagu <b> w opisie' } }];\nlet text = match[1].trim().replace(/&nbsp;/gi, ' ').replace(/\\s+/g, ' ');\nconst words = text.split(/\\s+/);\nconst shortWords = ['z','w','na','do','i','o','u','ze','po','za','a','oraz'];\nlet cutIndex = words.length - 2;\nif (shortWords.includes((words[words.length - 2] || '').toLowerCase())) cutIndex = words.length - 1;\nconst trimmed = words.length > 2 ? words.slice(0, cutIndex).join(' ') + ' ...' : '...';\nreturn [{ json: { result: `<b>${trimmed}</b>` } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4432,
        3488
      ],
      "id": "b10cd56f-6bad-4a31-93ed-8c5423e18413",
      "name": "Pobierz Szablon Opisu2"
    },
    {
      "parameters": {
        "jsCode": "// Przeka\u017c wybran\u0105 miniaturk\u0119 do pipeline'u obrazowego\nconst src = $('Wybierz Miniaturk\u01191').first();\nconst webhookBinary = $('Webhook - Odbierz Produkty').first().binary || {};\nconst thumbKey = src.json.chosenThumbnailKey;\nif (!thumbKey || !webhookBinary[thumbKey]) {\n  throw new Error('[B\u0141\u0104D] Brak wybranej miniaturki w binary webhooka.');\n}\nreturn { json: src.json, binary: { image: webhookBinary[thumbKey] } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4288,
        3616
      ],
      "id": "6f1dd001-d6e1-4559-b42e-b03846314b73",
      "name": "Wyodr\u0119bnij Obraz1"
    },
    {
      "parameters": {
        "jsCode": "const { thumbnailEntries, regularEntries, firstImageName } = $input.item.json;\nconst webhookBinary = $('Webhook - Odbierz Produkty').first().binary || {};\n\nfunction parseFileSize(sizeStr) {\n  if (!sizeStr) return 0;\n  const match = sizeStr.match(/([\\d.]+)\\s*(KB|MB|GB|B)/i);\n  if (!match) return 0;\n  const val = parseFloat(match[1]);\n  const unit = match[2].toUpperCase();\n  if (unit === 'GB') return val * 1024 * 1024 * 1024;\n  if (unit === 'MB') return val * 1024 * 1024;\n  if (unit === 'KB') return val * 1024;\n  return val;\n}\n\nlet chosenKey      = thumbnailEntries[0].key;\nlet chosenFileName = thumbnailEntries[0].fileName;\nlet chosenPrefix   = thumbnailEntries[0].prefix;\nlet chosenSkuRaw   = thumbnailEntries[0].skuRaw;\n\nif (thumbnailEntries.length > 1) {\n  let bestSize = -Infinity;\n\n  for (const entry of thumbnailEntries) {\n    const binData = webhookBinary[entry.key];\n    if (!binData) continue;\n\n    const sizeBytes = parseFileSize(binData.fileSize);\n    console.log(`[${entry.fileName}] fileSize: ${binData.fileSize} = ${sizeBytes} bajt\u00f3w`);\n\n    if (sizeBytes > bestSize) {\n      bestSize       = sizeBytes;\n      chosenKey      = entry.key;\n      chosenFileName = entry.fileName;\n      chosenPrefix   = entry.prefix;\n      chosenSkuRaw   = entry.skuRaw;\n    }\n  }\n\n  console.log(`Wybrano: ${chosenFileName}`);\n}\n\nreturn {\n  json: {\n    thumbnailEntries,\n    regularEntries,\n    firstImageName,\n    chosenThumbnailKey:      chosenKey,\n    chosenThumbnailFileName: chosenFileName,\n    skuPrefix:  chosenPrefix,\n    skuRaw:     chosenSkuRaw\n  },\n  binary: {\n    image: webhookBinary[chosenKey]\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4416,
        3968
      ],
      "id": "9690996e-82d8-4f2c-b867-57297fb225f5",
      "name": "Wybierz Miniaturk\u01191"
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// PARSUJ NAZWY PLIK\u00d3W Z BINARY WEBHOOKA\n//\n// Obs\u0142ugiwane formaty nazw:\n//   DN-271_min_boy.jpg   \u2192 miniaturka (boy)\n//   DN-271_min_girl.jpg  \u2192 miniaturka (girl)\n//   214_001min_girl.jpg  \u2192 miniaturka (girl)\n//   214_001_girl.jpg     \u2192 zdj\u0119cie nr 1 / awaryjna miniaturka\n//   214_1l.jpg           \u2192 zdj\u0119cie nr 1 / awaryjna miniaturka\n//   271_2_baner.jpg      \u2192 zdj\u0119cie nr 2\n//   DN-261_5_opis_3.jpg  \u2192 zdj\u0119cie nr 5\n//\n// Regu\u0142y:\n//   - Segment po SKU zawiera \"min\" \u2192 miniaturka\n//   - Segment zaczyna si\u0119 cyfr\u0105 \u2192 numer pozycji zdj\u0119cia\n//   - Gdy nie ma miniaturki \"min\", zdj\u0119cie z najni\u017cszym numerem\n//     zostaje u\u017cyte jako miniaturka\n//   - Wszystko po pierwszym segmencie jest ignorowane\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst webhookBinary = $('Webhook - Odbierz Produkty').first().binary || {};\n\nfunction parseFileName(fileName) {\n  if (!fileName) return null;\n\n  const base = fileName.replace(/\\.[^.]+$/, '');\n\n  // Opcjonalny prefix, SKU oraz pierwszy segment po SKU\n  //\n  // Przyk\u0142ady:\n  // DN-271_min_boy  \u2192 prefix: DN-, SKU: 271, segment: min\n  // 214_001_girl    \u2192 prefix: \"\", SKU: 214, segment: 001\n  // 214_1l          \u2192 prefix: \"\", SKU: 214, segment: 1l\n  const match = base.match(/^([A-Za-z]+-)?([0-9]+)_([^_]+)/);\n\n  if (!match) return null;\n\n  const prefix = match[1] || '';\n  const skuRaw = match[2];\n  const segment = match[3].toLowerCase();\n\n  // Miniaturka, gdy segment zawiera:\n  // min, 001min, 1min itd.\n  const isThumbnail = /^[0-9]*min/i.test(segment);\n\n  // Pobierz cyfry znajduj\u0105ce si\u0119 na pocz\u0105tku segmentu:\n  // 001  \u2192 1\n  // 1l   \u2192 1\n  // 02x  \u2192 2\n  // baner \u2192 brak numeru\n  const numberMatch = segment.match(/^([0-9]+)/);\n\n  const imageNumber = isThumbnail || !numberMatch\n    ? null\n    : parseInt(numberMatch[1], 10);\n\n  // P\u0142e\u0107 mo\u017ce znajdowa\u0107 si\u0119 w dowolnym miejscu nazwy\n  const gender = /girl/i.test(base)\n    ? 'girl'\n    : (/boy/i.test(base) ? 'boy' : null);\n\n  return {\n    prefix,\n    skuRaw,\n    segment,\n    isThumbnail,\n    imageNumber,\n    gender\n  };\n}\n\nconst allKeys = Object.keys(webhookBinary)\n  .filter(key => /^images\\d+$/.test(key));\n\nconst thumbnailEntries = [];\nconst regularEntries = [];\n\nfor (const key of allKeys) {\n  const fileName = webhookBinary[key]?.fileName || '';\n  const parsed = parseFileName(fileName);\n\n  if (!parsed) continue;\n\n  const entry = {\n    key,\n    fileName,\n    parsed\n  };\n\n  if (parsed.isThumbnail) {\n    thumbnailEntries.push(entry);\n  } else if (parsed.imageNumber !== null) {\n    // Dodajemy tylko pliki, z kt\u00f3rych uda\u0142o si\u0119 odczyta\u0107 numer\n    regularEntries.push(entry);\n  }\n}\n\n// Sortowanie zdj\u0119\u0107 wed\u0142ug numeru.\n// Przy identycznym numerze zachowujemy dodatkowo kolejno\u015b\u0107 kluczy images1,\n// images2 itd.\nregularEntries.sort((a, b) => {\n  const numberDifference =\n    a.parsed.imageNumber - b.parsed.imageNumber;\n\n  if (numberDifference !== 0) {\n    return numberDifference;\n  }\n\n  const keyNumberA = parseInt(a.key.replace(/\\D/g, ''), 10) || 0;\n  const keyNumberB = parseInt(b.key.replace(/\\D/g, ''), 10) || 0;\n\n  return keyNumberA - keyNumberB;\n});\n\n// Awaryjna miniaturka:\n// je\u017celi nie znaleziono pliku z \"min\", wybierz zdj\u0119cie\n// z najni\u017cszym numerem.\nlet usedFallbackThumbnail = false;\n\nif (thumbnailEntries.length === 0) {\n  if (regularEntries.length === 0) {\n    throw new Error(\n      '[B\u0141\u0104D] Nie znaleziono miniaturki ani \u017cadnego zdj\u0119cia z numerem w nazwie.'\n    );\n  }\n\n  const fallbackEntry = regularEntries.shift();\n\n  fallbackEntry.parsed.isThumbnail = true;\n  thumbnailEntries.push(fallbackEntry);\n\n  usedFallbackThumbnail = true;\n}\n\nreturn {\n  json: {\n    thumbnailEntries: thumbnailEntries.map(entry => ({\n      key: entry.key,\n      fileName: entry.fileName,\n      prefix: entry.parsed.prefix,\n      skuRaw: entry.parsed.skuRaw,\n      gender: entry.parsed.gender,\n      fallback: usedFallbackThumbnail\n    })),\n\n    regularEntries: regularEntries.map(entry => ({\n      key: entry.key,\n      fileName: entry.fileName,\n      imageNumber: entry.parsed.imageNumber,\n      gender: entry.parsed.gender\n    })),\n\n    firstImageName: thumbnailEntries[0].fileName,\n    usedFallbackThumbnail\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -4640,
        4016
      ],
      "id": "761d9710-fdbe-4271-a794-b72390ebca23",
      "name": "Parsuj Nazwy Plik\u00f3w1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "ai-check",
              "leftValue": "={{ $('Webhook - Odbierz Produkty').item.json.body.useAI }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -4880,
        4208
      ],
      "id": "c30998d7-8207-49e5-a3c6-f215e5d6720b",
      "name": "If11"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventoryProductsData"
            },
            {
              "name": "parameters",
              "value": "={\"inventory_id\": {{ $('Pobierz ID Katalog\u00f3w3').item.json.inventories[0].inventory_id }},\"products\":[{{ Object.keys($json.products)[0] }}]}"
            }
          ]
        },
        "options": {}
      },
      "name": "2. Pobierz pe\u0142ne dane3",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5280,
        4256
      ],
      "id": "8cc3ac2b-8fbb-466e-849f-a1be729c3b44"
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "resource": "message",
        "guildId": {
          "__rl": true,
          "value": "={{ $env.DISCORD_GUILD_ID }}",
          "mode": "id"
        },
        "channelId": {
          "__rl": true,
          "value": "={{ $env.DISCORD_CHANNEL_ID }}",
          "mode": "id"
        },
        "content": "Nie znaleziono szablonowego SKU",
        "options": {}
      },
      "type": "n8n-nodes-base.discord",
      "typeVersion": 2,
      "position": [
        -5120,
        4560
      ],
      "id": "25dfa24c-4666-4494-b49f-063acd0e5c74",
      "name": "Send a message3"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\n  \"error\": \"Nie znaleziono SKU szablonu\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        -5296,
        4560
      ],
      "id": "a1a47392-cbd5-4b89-b7c1-a2b8b7e823dd",
      "name": "Zwr\u00f3\u0107 b\u0142\u0105d nie znalezienia SKU1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "sku-check",
              "leftValue": "={{ $json.products }}",
              "rightValue": "",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -5456,
        4272
      ],
      "id": "969a9b07-48be-4b99-8663-0c070a5aefd2",
      "name": "Czy znaleziono sku3"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventoryProductsList"
            },
            {
              "name": "parameters",
              "value": "={\"inventory_id\": {{ $json.inventories[0].inventory_id }},\"filter_sku\": \"{{ $('Webhook - Odbierz Produkty').item.json.body.sku }}\"}"
            }
          ]
        },
        "options": {}
      },
      "name": "1. Znajd\u017a po SKU3",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5616,
        4256
      ],
      "id": "aad4cf8e-5f9f-4330-9078-1c240bc2c901"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.baselinker.com/connector.php",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BLToken",
              "value": "={{ $env.BASELINKER_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "method",
              "value": "getInventories"
            }
          ]
        },
        "options": {}
      },
      "name": "Pobierz ID Katalog\u00f3w3",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -5824,
        4272
      ],
      "id": "173d1b06-81b5-4b9e-8332-0bf45e877a77"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "action-check",
              "leftValue": "={{ $json.body.action }}",
              "rightValue": "checkdescription",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -6128,
        4576
      ],
      "id": "e1bdb183-c5ec-43e0-b056-c6e341deb2eb",
      "name": "If12"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\n  \"error\": \"Podane has\u0142o jest nieprawid\u0142owe\"\n}",
        "options": {
          "responseCode": 401
        }
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        -6128,
        4736
      ],
      "id": "605bc562-f53d-4d86-9e5b-9f5899cfab00",
      "name": "Respond to Webhook6"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "pwd-check",
              "leftValue": "={{ $json.body.password || '' }}",
              "rightValue": "={{ $env.WORKFLOW_ACCESS_PASSWORD }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -6416,
        4752
      ],
      "id": "f3d3471a-3d85-48dc-8597-ae040f71bd64",
      "name": "Sprawdz has\u0142o1"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "baselinker-ai",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "={{ $env.WEBHOOK_ALLOWED_ORIGIN }}"
        }
      },
      "id": "12f314c7-0f9b-4160-80e4-e388809c8d13",
      "name": "Webhook - Odbierz Produkty",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -6592,
        4576
      ]
    },
    {
      "parameters": {
        "mode": "chooseBranch",
        "useDataOfInput": 2
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -4128,
        3328
      ],
      "id": "9a181ff5-84b0-4dab-ba35-476352046461",
      "name": "Po\u0142\u0105cz Dane1"
    },
    {
      "parameters": {
        "operation": "resize",
        "dataPropertyName": "=image",
        "width": 150,
        "height": 150,
        "resizeOption": "onlyIfLarger",
        "options": {}
      },
      "type": "n8n-nodes-base.editImage",
      "typeVersion": 1,
      "position": [
        -3904,
        3328
      ],
      "id": "67f725ad-6c61-41d6-a208-36e71fcaa8b2",
      "name": "Zmie\u0144 Rozmiar2"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "init-01",
              "name": "titleAttempt",
              "value": 0,
              "type": "number"
            },
            {
              "id": "init-02",
              "name": "currentTitle",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-03",
              "name": "lastTitleReason",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-04",
              "name": "lastTitleSuggestion",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-05",
              "name": "descAttempt",
              "value": 0,
              "type": "number"
            },
            {
              "id": "init-06",
              "name": "currentDesc",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-07",
              "name": "lastDescReason",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-08",
              "name": "lastDescSuggestion",
              "value": "",
              "type": "string"
            },
            {
              "id": "init-09",
              "name": "szablon",
              "value": "={{ $('Pobierz Szablon Opisu2').isExecuted ? $('Pobierz Szablon Opisu2').first().json.result : $('Pobierz Szablon Opisu1').first().json.result }}",
              "type": "string"
            },
            {
              "id": "init-10",
              "name": "nazwaPliku",
              "value": "={{ $('Wybierz Miniaturk\u01191').first().json.chosenThumbnailFileName || '' }}",
              "type": "string"
            },
            {
              "id": "init-11",
              "name": "tytulSzablon",
              "value": "={{ Object.values($('2. Pobierz pe\u0142ne dane3').first().json.products || {})?.[0]?.text_fields?.name || '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -3680,
        3328
      ],
      "id": "2002ee56-bda5-4a36-aa29-ac3834d0567d",
      "name": "Inicjalizuj Stan1"
    },
    {
      "parameters": {
        "jsCode": "const state = { ...$input.item.json };\nlet imageBinary;\nif ($input.item.binary?.image) {\n  imageBinary = $input.item.binary.image;\n} else {\n  const wb = $('Webhook - Odbierz Produkty').first().binary || {};\n  const k = $('Wybierz Miniaturk\u01191').first().json.chosenThumbnailKey;\n  if (!k) throw new Error('[B\u0141\u0104D] Brak klucza miniaturki.');\n  imageBinary = wb[k];\n}\nreturn { json: state, binary: { image: imageBinary } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -3216,
        3360
      ],
      "id": "315dd526-600d-45a2-80c6-ad2e46e1ccdd",
      "name": "Wstrzyknij Obraz T1"
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "analyze",
        "modelId": {
          "__rl": true,
          "value": "models/gemini-2.5-flash-lite",
          "mode": "list"
        },
        "text": "=Jeste\u015b ekspertem od tworzenia tytu\u0142\u00f3w produkt\u00f3w na Allegro.\n\n{{ $json.titleAttempt > 0 ? '\u26a0\ufe0f POPRZEDNIA PR\u00d3BA NR ' + $json.titleAttempt + ' ODRZUCONA\\nOdrzucony tytu\u0142: \"' + $json.currentTitle + '\"\\nPow\u00f3d odrzucenia: ' + $json.lastTitleReason + ($json.lastTitleSuggestion ? '\\nSugestia poprawki: ' + $json.lastTitleSuggestion : '') + '\\n\\nUWZGL\u0118DNIJ powy\u017csze uwagi i wygeneruj POPRAWIONY tytu\u0142.\\n' : '' }}\nZADANIE: Stw\u00f3rz NOWY tytu\u0142 produktu inspirowany szablonem \u2014 NIE kopiuj szablonu.\n\nDANE WEJ\u015aCIOWE:\n- Nazwa pliku: {{ $('Inicjalizuj Stan1').item.json.nazwaPliku }}\n- Szablon (wzorzec do na\u015bladowania, NIE gotowa odpowied\u017a): {{ $('Inicjalizuj Stan1').item.json.tytulSzablon }}\n\nKROK 1 \u2014 OKRE\u015aL P\u0141E\u0106 (obowi\u0105zkowe, sprawd\u017a w tej kolejno\u015bci):\n- Nazwa pliku zawiera \"girl\" \u2192 MUSISZ u\u017cy\u0107 \"dla Dziewczynki\"\n- Nazwa pliku zawiera \"boy\" \u2192 MUSISZ u\u017cy\u0107 \"dla Ch\u0142opca\"\n- Brak girl/boy \u2192 oce\u0144 obraz i wybierz odpowiednie okre\u015blenie LUB JE\u017bELI UWA\u017bASZ ZE PASUJE DLA DW\u00d3CH P\u0141CI ZOSTAW \"Dla Dzieci\"\n\nKROK 2 \u2014 OKRE\u015aL MOTYW z obrazu:\n- Sp\u00f3jrz na obraz i nazwij JEDEN g\u0142\u00f3wny motyw (np. Lisek, Jednoro\u017cec, Dinozaur)\n- Motyw MUSI by\u0107 w MIANOWNIKU\n- NIE u\u017cywaj motywu z szablonu \u2014 u\u017cyj tego co widzisz na obrazie\n\nKROK 3 \u2014 ZBUDUJ TYTU\u0141:\n- We\u017a struktur\u0119 szablonu (kolejno\u015b\u0107 s\u0142\u00f3w, styl)\n- Zamie\u0144 w szablonie: stare okre\u015blenie p\u0142ci \u2192 nowe z Kroku 1\n- Zamie\u0144 w szablonie: stare s\u0142owo motywu (ostatnie s\u0142owo) \u2192 nowe z Kroku 2\n- Sprawd\u017a czy tytu\u0142 ma max 120 znak\u00f3w\n\nSZABLON TO WZORZEC \u2014 jego ostatnie s\u0142owo (motyw) i okre\u015blenie p\u0142ci ZAWSZE musz\u0105 zosta\u0107 zmienione.\n\nZwr\u00f3\u0107 WY\u0141\u0104CZNIE gotowy tytu\u0142 \u2014 jedna linia, bez komentarzy, bez cudzys\u0142ow\u00f3w.",
        "inputType": "binary",
        "binaryPropertyName": "image",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        -2976,
        3360
      ],
      "id": "5e699b3f-2584-401b-90c8-ac3838e9125d",
      "name": "Generuj Tytu\u01421"
    },
    {
      "parameters": {
        "jsCode": "const generatedTitle = ($input.item.json.content?.parts?.[0]?.text || '').trim().replace(/\\n/g, ' ');\nconst state = $('Wstrzyknij Obraz T1').item.json;\nreturn { json: { ...state, currentTitle: generatedTitle } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2768,
        3392
      ],
      "id": "a1fc04b3-fb94-436d-8933-f58fb514a508",
      "name": "Zapisz Kandydata T1"
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "models/gemini-3.1-flash-lite-preview",
          "mode": "list"
        },
        "messages": {
          "values": [
            {
              "content": "=Jeste\u015b weryfikatorem tytu\u0142\u00f3w. Oceniasz WY\u0141\u0104CZNIE wed\u0142ug podanych kryteri\u00f3w.\n\nTYTU\u0141 DO WERYFIKACJI: \"{{ $json.currentTitle }}\"\nNAZWA PLIKU: \"{{ $('Inicjalizuj Stan1').item.json.nazwaPliku }}\"\n\nKRYTERIA:\n\nODBIORCA:\nTytu\u0142 musi zawiera\u0107 przynajmniej jedno poprawne okre\u015blenie odbiorcy.\n\nZasady:\n- Je\u015bli plik zawiera \"girl\" \u2192 wymagane \"dla Dziewczynki\" LUB \"dla Dzieci Dziewczynki\"\n- Je\u015bli plik zawiera \"boy\" \u2192 wymagane \"dla Ch\u0142opca\" LUB \"dla Dzieci Ch\u0142opca\"\n- Je\u015bli brak \"girl\"/\"boy\" \u2192 wymagane \"dla Dzieci\"\n\nNiedozwolone:\n- Brak jakiegokolwiek okre\u015blenia odbiorcy\n- Wi\u0119cej ni\u017c dwa okre\u015blenia odbiorcy \u0142\u0105cznie\n- Konstrukcje inne ni\u017c wymienione powy\u017cej (np. samo \"Ch\u0142opca\" bez \"dla\")\n\nWA\u017bNE przy liczeniu okre\u015ble\u0144: \"dla Dzieci Ch\u0142opca\" traktuj jako JEDNO okre\u015blenie, nie dwa.\n\nMOTYW:\nTytu\u0142 musi zawiera\u0107 konkretny rzeczownik w mianowniku opisuj\u0105cy fizyczny motyw wizualny, np.:\n- zwierz\u0119 (np. lis, kot, dinozaur)\n- posta\u0107 (np. ksi\u0119\u017cniczka, astronauta)\n- obiekt (np. samoch\u00f3d, balon, rakieta)\n\nNiedozwolone s\u0105 motywy abstrakcyjne lub og\u00f3lne, np.:\n\"podr\u00f3\u017ce\", \"praca\", \"marzenia\", \"zwiedzanie\", \"styl \u017cycia\" itp.\n\nD\u0141UGO\u015a\u0106:\nMaksymalnie 120 znak\u00f3w\n\nCZYSTO\u015a\u0106:\nBrak:\n- cudzys\u0142ow\u00f3w\n- nawias\u00f3w\n- emoji\n- wielokropk\u00f3w\n\nOdpowiedz WY\u0141\u0104CZNIE w JSON (bez markdown):\n{\"valid\": true/false, \"reason\": \"...\", \"suggestion\": \"...\"}"
            }
          ]
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        -2544,
        3360
      ],
      "id": "71592255-86cb-417f-bde9-4a7ac3a3109a",
      "name": "Weryfikuj Tytu\u01421",
      "disabled": true
    },
    {
      "parameters": {
        "jsCode": "const raw = ($input.item.json.content?.parts?.[0]?.text || '').trim();\nconst clean = raw.replace(/^```json\\s*/im,'').replace(/^```\\s*/im,'').replace(/```\\s*$/im,'').trim();\nlet parsed;\ntry { parsed = JSON.parse(clean); } catch(e) {\n  const m = clean.match(/\"valid\"\
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

BaseLinker Product Automation - Full Sanitized Workflow. Uses googleGemini, editImage, httpRequest, discord. Webhook trigger; 89 nodes.

Source: https://github.com/Over7oader/baselinker-ai-listing-automation/blob/fa3c06c06aa4af6dddecc2c0141a218b30faf62e/workflows/baselinker-product-automation-full.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

runout. Uses httpRequest, googleGemini, discord. Webhook trigger; 10 nodes.

HTTP Request, Google Gemini, Discord
AI & RAG

This workflow captures new leads via webhook, enriches and scores them with Apollo and Google Gemini, logs everything in Google Sheets, and automates outreach, reply handling, follow-ups, meeting prep

HTTP Request, Google Gemini, Google Sheets +3
AI & RAG

Postagem Redes — Portal: Ações. Uses openAi, googleGemini, ollama, httpRequest. Webhook trigger; 58 nodes.

OpenAI, Google Gemini, Ollama +4
AI & RAG

This workflow helps you repurpose your YouTube videos across multiple social media platforms with zero manual effort. It’s designed for creators, businesses, and marketers who want to maximize reach w

HTTP Request, RSS Feed Read, Discord +4
AI & RAG

This workflow automates the process of generating stylized product photos for e-commerce by combining real product shots with creative templates. It enables the creation of a complete set of images fo

Airtable, HTTP Request, Google Gemini