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 →
{
"name": "BaseLinker AI Listing Automation",
"nodes": [
{
"id": "webhook-main",
"name": "Webhook - Odbierz Produkty",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-400,
200
],
"parameters": {
"path": "baselinker-ai",
"httpMethod": "POST",
"responseMode": "responseNode",
"options": {}
}
},
{
"id": "validate-input",
"name": "Waliduj Input",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-200,
200
],
"parameters": {
"jsCode": "// Walidacja wej\u015bciowych danych z frontendu\nconst body = $input.first().json.body || $input.first().json;\nconst products = body.products;\n\nif (!products || !Array.isArray(products) || products.length === 0) {\n throw new Error('BRAK_PRODUKTOW: Payload musi zawiera\u0107 tablic\u0119 \"products\" z co najmniej jednym produktem.');\n}\n\nfor (const [i, p] of products.entries()) {\n if (!p.SKU_Szablonu) throw new Error(`BRAK_SKU: Produkt #${i+1} nie ma pola SKU_Szablonu.`);\n if (!p.Zdjecie_Szukaj) throw new Error(`BRAK_ZDJECIA: Produkt #${i+1} nie ma pola Zdjecie_Szukaj.`);\n if (!p.images || p.images.length === 0) throw new Error(`BRAK_OBRAZOW: Produkt #${i+1} (${p.SKU_Szablonu}) nie ma \u017cadnych zdj\u0119\u0107.`);\n if (!p.Stare_Slowo || !p.Nowe_Slowo) throw new Error(`BRAK_SLOW: Produkt #${i+1} (${p.SKU_Szablonu}) nie ma Stare_Slowo / Nowe_Slowo.`);\n}\n\nreturn products.map(p => ({ json: p }));"
}
},
{
"id": "loop-products",
"name": "Petla Produktow",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
0,
200
],
"parameters": {
"batchSize": 1,
"options": {}
}
},
{
"id": "set-config",
"name": "Konfiguracja API",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [
200,
200
],
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "cfg-bl-token",
"name": "BL_TOKEN",
"value": "={{ $env.BASELINKER_TOKEN }}",
"type": "string"
},
{
"id": "cfg-inv-id",
"name": "INVENTORY_ID",
"value": "={{ $env.BASELINKER_INVENTORY_ID }}",
"type": "string"
},
{
"id": "cfg-gemini",
"name": "GEMINI_KEY",
"value": "={{ $env.GEMINI_API_KEY }}",
"type": "string"
},
{
"id": "cfg-price-group",
"name": "PRICE_GROUP_ID",
"value": "={{ $env.BASELINKER_PRICE_GROUP_ID }}",
"type": "string"
},
{
"id": "cfg-warehouse",
"name": "WAREHOUSE_ID",
"value": "={{ $env.BASELINKER_WAREHOUSE_ID }}",
"type": "string"
}
]
},
"includeOtherFields": true
}
},
{
"id": "extract-prefix",
"name": "Oblicz Prefix SKU",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
200
],
"parameters": {
"jsCode": "const item = $input.item.json;\nconst sku = item.SKU_Szablonu || '';\n\n// Wyci\u0105gamy prefix literowy np. 'DN-' z 'DN-0101'\nconst match = sku.match(/^([A-Za-z]+-?)/);\nconst prefix = match ? match[1] : sku.substring(0, 3);\n\nreturn {\n json: {\n ...item,\n _prefix: prefix,\n _templateSku: sku\n }\n};"
}
},
{
"id": "bl-list-by-prefix",
"name": "BL: Pobierz produkty wg prefixu",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
600,
200
],
"parameters": {
"method": "POST",
"url": "https://api.baselinker.com/connector.php",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-BLToken",
"value": "={{ $('Konfiguracja API').item.json.BL_TOKEN }}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "method",
"value": "getInventoryProductsList"
},
{
"name": "parameters",
"value": "={{ JSON.stringify({ inventory_id: $('Konfiguracja API').item.json.INVENTORY_ID, filter_sku: $('Oblicz Prefix SKU').item.json._prefix, page: 1 }) }}"
}
]
},
"options": {}
}
},
{
"id": "find-template-and-next-sku",
"name": "Znajdz Szablon i Oblicz Nastepne SKU",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
200
],
"parameters": {
"jsCode": "const blResp = $input.item.json;\nconst prefix = $('Oblicz Prefix SKU').item.json._prefix;\nconst templateSku = $('Oblicz Prefix SKU').item.json._templateSku;\nconst products = blResp.products || {};\n\nif (blResp.status !== 'SUCCESS' && !blResp.products) {\n throw new Error(`BL_ERROR: getInventoryProductsList zwr\u00f3ci\u0142 b\u0142\u0105d: ${JSON.stringify(blResp)}`);\n}\n\n// Szukamy product_id dla template SKU (np. DN-0101)\nlet templateProductId = null;\nlet maxNum = 0;\n\nfor (const [pid, pdata] of Object.entries(products)) {\n const sku = pdata.sku || '';\n if (sku === templateSku) {\n templateProductId = pid;\n }\n // Liczymy MAX numer dla tego prefixu\n const numStr = sku.replace(prefix, '');\n const num = parseInt(numStr, 10);\n if (!isNaN(num) && num > maxNum) maxNum = num;\n}\n\nif (!templateProductId) {\n throw new Error(`BRAK_SZABLONU: Nie znaleziono produktu o SKU '${templateSku}' w BaseLinker. Sprawd\u017a czy SKU_Szablonu w CSV jest poprawny.`);\n}\n\nconst nextNum = maxNum + 1;\nconst newSku = prefix + nextNum.toString().padStart(4, '0');\n\nreturn {\n json: {\n ...$('Oblicz Prefix SKU').item.json,\n _templateProductId: templateProductId,\n _newSku: newSku,\n _maxFound: maxNum\n }\n};"
}
},
{
"id": "bl-get-template-data",
"name": "BL: Pobierz Dane Szablonu",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1000,
200
],
"parameters": {
"method": "POST",
"url": "https://api.baselinker.com/connector.php",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-BLToken",
"value": "={{ $('Konfiguracja API').item.json.BL_TOKEN }}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "method",
"value": "getInventoryProductsData"
},
{
"name": "parameters",
"value": "={{ JSON.stringify({ inventory_id: $('Konfiguracja API').item.json.INVENTORY_ID, products: [ $('Znajdz Szablon i Oblicz Nastepne SKU').item.json._templateProductId ] }) }}"
}
]
},
"options": {}
}
},
{
"id": "prep-gemini",
"name": "Przygotuj Prompt Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1200,
200
],
"parameters": {
"jsCode": "const blData = $input.item.json;\nconst ctx = $('Znajdz Szablon i Oblicz Nastepne SKU').item.json;\nconst templateId = ctx._templateProductId;\n\nif (blData.status !== 'SUCCESS') {\n throw new Error(`BL_TEMPLATE_ERROR: ${JSON.stringify(blData)}`);\n}\n\nconst template = blData.products[templateId];\nif (!template) {\n throw new Error(`BRAK_DANYCH_SZABLONU: Nie ma danych dla product_id=${templateId}`);\n}\n\nconst templateName = template.text_fields?.name || template.name || '';\nconst templateDesc = template.text_fields?.description || template.description || '';\nconst templateDesc1 = template.text_fields?.description_extra1 || '';\nconst templateDesc2 = template.text_fields?.description_extra2 || '';\nconst templateDesc3 = template.text_fields?.description_extra3 || '';\nconst templateDesc4 = template.text_fields?.description_extra4 || '';\n\nconst stareSlowo = ctx.Stare_Slowo || '';\nconst noweSlowo = ctx.Nowe_Slowo || '';\nconst audience = ctx.Audience || 'unknown';\nconst firstImageB64 = ctx.images[0];\n\nconst prompt = `Jeste\u015b copywriterem SEO dla e-commerce (Allegro/marketplace PL). Twoje zadanie to przepisanie karty produktu naklejek \u015bciennych.\n\nSZABLON TYTU\u0141U: ${templateName}\nSZABLON OPISU (HTML): ${templateDesc}\n\nSTARE S\u0141OWO (do usuni\u0119cia): ${stareSlowo}\nNOWE S\u0141OWO (do wprowadzenia): ${noweSlowo}\nP\u0141E\u0106 odbiorcy (z pliku): ${audience} \u2014 je\u015bli 'unknown', wywnioskuj ze zdj\u0119cia.\n\nZASADY:\n1. Zachowaj DOK\u0141ADNIE t\u0119 sam\u0105 struktur\u0119 HTML \u2014 nie zmieniaj tag\u00f3w, klas, sekcji.\n2. Zamie\u0144 KA\u017bD\u0104 form\u0119 gramatyczn\u0105 starego s\u0142owa na odpowiedni\u0105 form\u0119 nowego s\u0142owa (odmiana przez przypadki PL!).\n3. W tytule: zachowaj wzorzec 'Naklejki na \u015acian\u0119 dla Dzieci [Ch\u0142opca/Dziewczynki] \u015acienne Samoprzylepne Naklejka [Nowe Slowo]'.\n4. W opisie: zamie\u0144 'z ${stareSlowo.toLowerCase()}ami', 'z ${stareSlowo.toLowerCase()}iem' itp. na poprawn\u0105 form\u0119 '${noweSlowo}'.\n5. Dopasuj zwroty dla/ch\u0142opca/dziewczynki je\u015bli audience to nie 'unknown'.\n6. NIE zmieniaj parametr\u00f3w technicznych (wymiary, materia\u0142, itp.).\n7. Dla description_extra1-4: przepisz analogicznie zachowuj\u0105c HTML.\n\nODPOWIED\u0179: Tylko czysty JSON (bez markdown, bez ```json):\n{\"title\": \"...\", \"description\": \"...\", \"description_extra1\": \"...\", \"description_extra2\": \"...\", \"description_extra3\": \"...\", \"description_extra4\": \"...\"}`;\n\nreturn {\n json: {\n ...ctx,\n _templateWeight: template.weight || 2,\n _templateTaxRate: template.tax_rate || 23,\n _templateCategoryId: template.category_id || '',\n _templatePrice: template.prices ? Object.values(template.prices)[0] : 47.99,\n _templateStock: template.stock ? Object.values(template.stock)[0] : 100,\n _templateDesc1: templateDesc1,\n _templateDesc2: templateDesc2,\n _templateDesc3: templateDesc3,\n _templateDesc4: templateDesc4,\n _geminiPrompt: prompt,\n _firstImageB64: firstImageB64\n }\n};"
}
},
{
"id": "gemini-vision",
"name": "Gemini Vision - Generuj Tytul i Opis",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1400,
200
],
"parameters": {
"method": "POST",
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "x-goog-api-key",
"value": "={{ $('Konfiguracja API').item.json.GEMINI_KEY }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ contents: [{ parts: [{ text: $('Przygotuj Prompt Gemini').item.json._geminiPrompt }, { inline_data: { mime_type: 'image/jpeg', data: $('Przygotuj Prompt Gemini').item.json._firstImageB64 } }] }], generationConfig: { temperature: 0.3, maxOutputTokens: 4096 } }) }}",
"options": {}
}
},
{
"id": "parse-gemini-response",
"name": "Parsuj Odpowiedz Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1600,
200
],
"parameters": {
"jsCode": "const geminiResp = $input.item.json;\nconst ctx = $('Przygotuj Prompt Gemini').item.json;\n\nif (!geminiResp.candidates || geminiResp.candidates.length === 0) {\n throw new Error(`GEMINI_ERROR: Brak odpowiedzi od Gemini. ${JSON.stringify(geminiResp)}`);\n}\n\nlet text = geminiResp.candidates[0]?.content?.parts?.[0]?.text || '';\n\n// Wyczy\u015b\u0107 potencjalny markdown\ntext = text.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n\nlet parsed;\ntry {\n parsed = JSON.parse(text);\n} catch(e) {\n // Spr\u00f3buj wyci\u0105gn\u0105\u0107 JSON z tekstu\n const match = text.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n parsed = JSON.parse(match[0]);\n } else {\n throw new Error(`GEMINI_PARSE_ERROR: Nie mo\u017cna sparsowa\u0107 JSON z Gemini. Odpowied\u017a: ${text.substring(0, 500)}`);\n }\n}\n\nif (!parsed.title || !parsed.description) {\n throw new Error(`GEMINI_INCOMPLETE: Brak title lub description w odpowiedzi Gemini.`);\n}\n\nreturn {\n json: {\n ...ctx,\n _aiTitle: parsed.title,\n _aiDesc: parsed.description,\n _aiDesc1: parsed.description_extra1 || ctx._templateDesc1,\n _aiDesc2: parsed.description_extra2 || ctx._templateDesc2,\n _aiDesc3: parsed.description_extra3 || ctx._templateDesc3,\n _aiDesc4: parsed.description_extra4 || ctx._templateDesc4\n }\n};"
}
},
{
"id": "bl-add-product",
"name": "BL: Dodaj Produkt",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1800,
200
],
"parameters": {
"method": "POST",
"url": "https://api.baselinker.com/connector.php",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-BLToken",
"value": "={{ $('Konfiguracja API').item.json.BL_TOKEN }}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "method",
"value": "addInventoryProduct"
},
{
"name": "parameters",
"value": "={{ JSON.stringify({\n inventory_id: $('Konfiguracja API').item.json.INVENTORY_ID,\n sku: $('Parsuj Odpowiedz Gemini').item.json._newSku,\n ean: '',\n weight: $('Parsuj Odpowiedz Gemini').item.json._templateWeight,\n tax_rate: $('Parsuj Odpowiedz Gemini').item.json._templateTaxRate,\n category_id: $('Parsuj Odpowiedz Gemini').item.json._templateCategoryId,\n text_fields: {\n name: $('Parsuj Odpowiedz Gemini').item.json._aiTitle,\n description: $('Parsuj Odpowiedz Gemini').item.json._aiDesc,\n description_extra1: $('Parsuj Odpowiedz Gemini').item.json._aiDesc1,\n description_extra2: $('Parsuj Odpowiedz Gemini').item.json._aiDesc2,\n description_extra3: $('Parsuj Odpowiedz Gemini').item.json._aiDesc3,\n description_extra4: $('Parsuj Odpowiedz Gemini').item.json._aiDesc4\n },\n prices: { [String($('Konfiguracja API').item.json.PRICE_GROUP_ID)]: Number($('Parsuj Odpowiedz Gemini').item.json.Cena) || Number($('Parsuj Odpowiedz Gemini').item.json._templatePrice) || 47.99 },\n stock: { [String($('Konfiguracja API').item.json.WAREHOUSE_ID)]: Number($('Parsuj Odpowiedz Gemini').item.json.Ilosc) || Number($('Parsuj Odpowiedz Gemini').item.json._templateStock) || 100 },\n images: $('Parsuj Odpowiedz Gemini').item.json.images.map(b64 => 'data:image/jpeg;base64,' + b64)\n}) }}"
}
]
},
"options": {}
}
},
{
"id": "check-product-added",
"name": "Sprawdz Wynik Dodania",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
200
],
"parameters": {
"jsCode": "const blResp = $input.item.json;\nconst ctx = $('Parsuj Odpowiedz Gemini').item.json;\n\nif (blResp.status !== 'SUCCESS' || !blResp.product_id) {\n throw new Error(`BL_ADD_ERROR: Nie uda\u0142o si\u0119 doda\u0107 produktu '${ctx._newSku}'. B\u0142\u0105d BaseLinker: ${JSON.stringify(blResp)}`);\n}\n\nconst newProductId = blResp.product_id;\n\nreturn {\n json: {\n _newSku: ctx._newSku,\n _newProductId: newProductId,\n _aiTitle: ctx._aiTitle,\n _audience: ctx.Audience,\n _imagesCount: ctx.images.length,\n status: 'SUCCESS',\n message: `Produkt '${ctx._newSku}' (ID: ${newProductId}) dodany pomy\u015blnie z ${ctx.images.length} zdj\u0119ciami. Tytu\u0142: ${ctx._aiTitle}`\n }\n};"
}
},
{
"id": "rate-limit-wait",
"name": "Poczekaj 2s (Rate Limit)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
2200,
200
],
"parameters": {
"amount": 2,
"unit": "seconds"
}
},
{
"id": "collect-results",
"name": "Zbierz Wyniki",
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
2400,
200
],
"parameters": {
"aggregate": "aggregateAllItemData",
"options": {}
}
},
{
"id": "respond-success",
"name": "Odpowiedz Sukces",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
2600,
200
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: true, processed: $input.all().length, results: $input.all().map(i => i.json) }) }}",
"options": {
"responseCode": 200
}
}
},
{
"id": "error-handler",
"name": "Odpowiedz Blad",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
600,
450
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: false, error: $input.first().json.message || 'Nieznany b\u0142\u0105d', step: $input.first().json.node || 'workflow' }) }}",
"options": {
"responseCode": 400
}
}
}
],
"connections": {
"Webhook - Odbierz Produkty": {
"main": [
[
{
"node": "Waliduj Input",
"type": "main",
"index": 0
}
]
]
},
"Waliduj Input": {
"main": [
[
{
"node": "Petla Produktow",
"type": "main",
"index": 0
}
]
]
},
"Petla Produktow": {
"main": [
[
{
"node": "Konfiguracja API",
"type": "main",
"index": 0
}
],
[
{
"node": "Zbierz Wyniki",
"type": "main",
"index": 0
}
]
]
},
"Konfiguracja API": {
"main": [
[
{
"node": "Oblicz Prefix SKU",
"type": "main",
"index": 0
}
]
]
},
"Oblicz Prefix SKU": {
"main": [
[
{
"node": "BL: Pobierz produkty wg prefixu",
"type": "main",
"index": 0
}
]
]
},
"BL: Pobierz produkty wg prefixu": {
"main": [
[
{
"node": "Znajdz Szablon i Oblicz Nastepne SKU",
"type": "main",
"index": 0
}
]
]
},
"Znajdz Szablon i Oblicz Nastepne SKU": {
"main": [
[
{
"node": "BL: Pobierz Dane Szablonu",
"type": "main",
"index": 0
}
]
]
},
"BL: Pobierz Dane Szablonu": {
"main": [
[
{
"node": "Przygotuj Prompt Gemini",
"type": "main",
"index": 0
}
]
]
},
"Przygotuj Prompt Gemini": {
"main": [
[
{
"node": "Gemini Vision - Generuj Tytul i Opis",
"type": "main",
"index": 0
}
]
]
},
"Gemini Vision - Generuj Tytul i Opis": {
"main": [
[
{
"node": "Parsuj Odpowiedz Gemini",
"type": "main",
"index": 0
}
]
]
},
"Parsuj Odpowiedz Gemini": {
"main": [
[
{
"node": "BL: Dodaj Produkt",
"type": "main",
"index": 0
}
]
]
},
"BL: Dodaj Produkt": {
"main": [
[
{
"node": "Sprawdz Wynik Dodania",
"type": "main",
"index": 0
}
]
]
},
"Sprawdz Wynik Dodania": {
"main": [
[
{
"node": "Poczekaj 2s (Rate Limit)",
"type": "main",
"index": 0
}
]
]
},
"Poczekaj 2s (Rate Limit)": {
"main": [
[
{
"node": "Petla Produktow",
"type": "main",
"index": 0
}
]
]
},
"Zbierz Wyniki": {
"main": [
[
{
"node": "Odpowiedz Sukces",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"errorWorkflow": "",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromAList"
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
BaseLinker AI Listing Automation. Uses httpRequest. Webhook trigger; 17 nodes.
Source: https://github.com/Over7oader/baselinker-ai-listing-automation/blob/fa3c06c06aa4af6dddecc2c0141a218b30faf62e/workflows/baselinker-ai-listing.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Spin up a tiny, serverless-style API from n8n that returns BTC/ETH prices & 24h changes plus USD→EUR and USD→NGN from public, no-key data sources. Ideal for dashboards, low-code apps, or internal tool
This n8n workflow automatically tracks hotel room prices, detects price drops, and sends real-time email alerts with savings calculations. It continuously monitors multiple hotels and room types to he
This workflow manually processes car photos from Google Drive, sends each image plus a studio backdrop and logo to the Google Gemini Image API for background replacement and license-plate branding, an
This workflow runs manually or daily to audit a public Etsy shop via the Etsy API, summarize active listing metrics, and store a snapshot in n8n to detect newly added or removed listings between runs.
3D Listing — covers (platforms split). Uses googleDrive, httpRequest. Event-driven trigger; 12 nodes.