Mała firma rzadko traci czas na jedno wielkie zadanie. Częściej znika on w dziesiątkach drobnych czynności: przepisaniu danych z formularza, sprawdzeniu płatności, wysłaniu przypomnienia, przekazaniu zamówienia do realizacji albo przygotowaniu raportu z końca tygodnia. n8n pomaga uporządkować takie procesy. Możesz połączyć formularze, skrzynkę mailową, arkusze, CRM, sklep internetowy, komunikator i zewnętrzne API w jeden workflow, czyli sekwencję przepływu pracy. Dzięki temu zadanie uruchamia się automatycznie po zdarzeniu albo o określonej godzinie.

W tym artykule znajdziesz cztery automatyzacje, które szczególnie dobrze pasują do małych firm. Każda pokazuje problem, rozwiązanie, benefit oraz prosty opis działania. Pod każdą sekcją zostawiliśmy też miejsce na kod workflow n8n, jaki możesz wkleić po wygenerowaniu i dostosowaniu do własnych narzędzi. Zanim zaczniesz polecam Ci także artykuł n8n – automatyzacja bez ograniczeń.

Jeśli chcesz uruchamiać automatyzacje na własnym środowisku, sprawdź VPS z preinstalowanym n8n. To wygodne rozwiązanie, gdy chcesz skupić się na budowaniu procesów, a nie na ręcznej konfiguracji infrastruktury.

4 gotowe automatyzacje, jakie znajdziesz w tym artykule:

1. Lead z formularza od razu w CRM

Formularz kontaktowy często jest pierwszym miejscem, gdzie potencjalny klient zostawia firmie swoje dane. To ważny moment, bo od szybkości reakcji zależy, czy rozmowa sprzedażowa zacznie się od razu, czy lead zdąży ostygnąć.

Automatyczna obsługa kontaktu od pierwszego zapytania. Proces

Problem. Klient wysyła formularz kontaktowy, ale wiadomość trafia tylko na skrzynkę. Ktoś musi ją przeczytać, przepisać dane do CRM, dopisać źródło zapytania i przekazać kontakt handlowcowi. Przy kilku zapytaniach dziennie to jeszcze działa. Przy większej liczbie leadów zaczynają pojawiać się opóźnienia, duplikaty i zgubione wiadomości.

Rozwiązanie. Workflow w n8n odbiera dane z formularza, sprawdza najważniejsze pola, zapisuje kontakt w CRM albo arkuszu, wysyła potwierdzenie do klienta i powiadamia zespół sprzedaży. Możesz dodać źródło kampanii, tag produktu i informację, kto powinien zająć się kontaktem.

Benefit. Reakcja na zapytanie jest szybsza, a dane trafiają w jedno miejsce. Handlowiec nie musi szukać informacji w mailach, a właściciel firmy widzi, skąd przychodzą leady i które kanały marketingowe działają najlepiej.

Co robi automatyzacja. Po wysłaniu formularza n8n odbiera imię, adres email, telefon, treść zapytania i źródło wejścia. Następnie porządkuje dane, sprawdza zgodę na kontakt, zapisuje rekord w CRM lub Google Sheets, wysyła klientowi krótkie potwierdzenie i publikuje powiadomienie dla zespołu sprzedaży.

Przykład w praktyce. Firma usługowa ma formularz wyceny na stronie. Gdy klient go wyśle, kontakt automatycznie trafia do arkusza „Nowe leady”, a handlowiec dostaje wiadomość: „Nowe zapytanie o wycenę. Źródło: kampania Google Ads. Termin kontaktu: dziś”.

Gotowa automatyzacja – Formularz leadowy do Google Sheets

Ta automatyzacja n8n odbiera dane z formularza przez Webhook, sprawdza obecność poprawnego adresu email oraz zgody marketingowej, a następnie zapisuje lead w Google Sheets. Poprawne zgłoszenia trafiają do zakładki Leads, a niekompletne rekordy do zakładki do weryfikacji. Dodatkowo workflow wysyła potwierdzenie do klienta oraz powiadomienie do zespołu sprzedaży.

Co trzeba zmienić, żeby to działało?

  • REPLACE_WITH_GOOGLE_SHEET_ID – podmień na ID swojego arkusza Google Sheets.
  • REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_ID i REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_NAME – wybierz własne credentiale Google Sheets w n8n.
  • REPLACE_WITH_SMTP_CREDENTIAL_ID i REPLACE_WITH_SMTP_CREDENTIAL_NAME – wybierz własne credentiale SMTP do wysyłki maili.
  • REPLACE_WITH_FROM_EMAIL@example.com – ustaw adres nadawcy wiadomości.
  • sales-team@example.com – podmień na adres email zespołu sprzedaży albo osoby odpowiedzialnej za leady.
  • W Google Sheets utwórz dwie zakładki: Leads oraz do weryfikacji.
  • W obu zakładkach dodaj nagłówki kolumn: timestamp, status, imie, email, telefon, wiadomosc, zrodlo_kampanii, zgoda_marketingowa, validation_errors, raw_payload.

Jak zainstalować automatyzację?

  1. Wejdź do n8n.
  2. Utwórz nowy workflow.
  3. Wklej JSON z sekcji poniżej.
  4. Wklej kod automatyzacji z bloku poniżej.
  5. Podmień placeholdery na własne dane.
  6. Podłącz credentiale Google Sheets oraz SMTP.
  7. Przetestuj Webhook, wysyłając przykładowe dane formularza.
  8. Po udanym teście aktywuj workflow.

Kod automatyzacji n8n

{
  "name": "Lead form webhook - Google Sheets + email notifications",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "lead-form",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "11111111-1111-4111-8111-111111111111",
      "name": "Webhook - formularz lead",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -760,
        0
      ],
      "webhookId": "d4b4c1d6-1ef2-4a11-b7e3-2c3b8a0f7a10"
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst body = input.body ?? input;\n\nconst getFirst = (...values) => {\n  for (const value of values) {\n    if (value !== undefined && value !== null && String(value).trim() !== '') {\n      return String(value).trim();\n    }\n  }\n  return '';\n};\n\nconst parseConsent = (value) => {\n  if (value === true || value === 1) return true;\n  if (value === false || value === 0 || value === null || value === undefined) return false;\n\n  const normalized = String(value).trim().toLowerCase();\n  return [\n    'true',\n    '1',\n    'yes',\n    'y',\n    'tak',\n    't',\n    'on',\n    'accepted',\n    'accept',\n    'zgoda',\n    'wyrazam zgode',\n    'wyrażam zgodę'\n  ].includes(normalized);\n};\n\nconst email = getFirst(body.email, body.mail, body['adres email'], body['e-mail']).toLowerCase();\nconst marketingConsent = parseConsent(\n  body.zgoda_marketingowa ??\n  body.zgodaMarketingowa ??\n  body.marketing_consent ??\n  body.marketingConsent ??\n  body.consent ??\n  body.zgoda ??\n  body['zgoda marketingowa']\n);\n\nconst hasEmail = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\nconst validationErrors = [];\n\nif (!hasEmail) validationErrors.push('Brak poprawnego emaila');\nif (!marketingConsent) validationErrors.push('Brak zgody marketingowej');\n\nreturn [\n  {\n    json: {\n      timestamp: new Date().toISOString(),\n      status: hasEmail && marketingConsent ? 'qualified' : 'do_weryfikacji',\n      imie: getFirst(body.imie, body['imię'], body.name, body.firstName, body.first_name),\n      email,\n      telefon: getFirst(body.telefon, body.phone, body.tel, body.mobile),\n      wiadomosc: getFirst(body.wiadomosc, body['wiadomość'], body.message, body.msg),\n      zrodlo_kampanii: getFirst(\n        body.zrodlo_kampanii,\n        body.zrodloKampanii,\n        body['źródło kampanii'],\n        body.campaign_source,\n        body.campaignSource,\n        body.utm_source,\n        body.source\n      ),\n      zgoda_marketingowa: marketingConsent,\n      has_email: hasEmail,\n      is_valid_lead: hasEmail && marketingConsent,\n      validation_errors: validationErrors.join('; '),\n      raw_payload: JSON.stringify(body)\n    }\n  }\n];"
      },
      "id": "22222222-2222-4222-8222-222222222222",
      "name": "Normalizuj i waliduj dane",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -520,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.is_valid_lead }}",
              "value2": true
            }
          ]
        }
      },
      "id": "33333333-3333-4333-8333-333333333333",
      "name": "Czy email i zgoda są obecne?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        -280,
        0
      ]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Leads",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "timestamp": "={{ $json.timestamp }}",
            "status": "={{ $json.status }}",
            "imie": "={{ $json.imie }}",
            "email": "={{ $json.email }}",
            "telefon": "={{ $json.telefon }}",
            "wiadomosc": "={{ $json.wiadomosc }}",
            "zrodlo_kampanii": "={{ $json.zrodlo_kampanii }}",
            "zgoda_marketingowa": "={{ $json.zgoda_marketingowa }}",
            "validation_errors": "={{ $json.validation_errors }}",
            "raw_payload": "={{ $json.raw_payload }}"
          },
          "matchingColumns": [
            "email"
          ]
        },
        "options": {}
      },
      "id": "44444444-4444-4444-8444-444444444444",
      "name": "Google Sheets - dodaj lub zaktualizuj lead",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        0,
        -140
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "REPLACE_WITH_FROM_EMAIL@example.com",
        "toEmail": "={{ $('Normalizuj i waliduj dane').item.json.email }}",
        "subject": "Dziękujemy za kontakt",
        "emailFormat": "html",
        "html": "=<p>Cześć {{ $('Normalizuj i waliduj dane').item.json.imie || '' }},</p><p>Dziękujemy za przesłanie formularza. Otrzymaliśmy Twoją wiadomość i nasz zespół skontaktuje się z Tobą w najbliższym możliwym terminie.</p><p>Pozdrawiamy,<br>Zespół sprzedaży</p>",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "55555555-5555-4555-8555-555555555555",
      "name": "Email - potwierdzenie do klienta",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        260,
        -140
      ],
      "credentials": {
        "smtp": {
          "id": "REPLACE_WITH_SMTP_CREDENTIAL_ID",
          "name": "REPLACE_WITH_SMTP_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "REPLACE_WITH_FROM_EMAIL@example.com",
        "toEmail": "sales-team@example.com",
        "subject": "=Nowy lead: {{ $('Normalizuj i waliduj dane').item.json.email }}",
        "emailFormat": "html",
        "html": "=<h3>Nowy lead z formularza</h3><ul><li><strong>Imię:</strong> {{ $('Normalizuj i waliduj dane').item.json.imie }}</li><li><strong>Email:</strong> {{ $('Normalizuj i waliduj dane').item.json.email }}</li><li><strong>Telefon:</strong> {{ $('Normalizuj i waliduj dane').item.json.telefon }}</li><li><strong>Źródło kampanii:</strong> {{ $('Normalizuj i waliduj dane').item.json.zrodlo_kampanii }}</li><li><strong>Zgoda marketingowa:</strong> {{ $('Normalizuj i waliduj dane').item.json.zgoda_marketingowa }}</li></ul><p><strong>Wiadomość:</strong><br>{{ $('Normalizuj i waliduj dane').item.json.wiadomosc }}</p>",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "66666666-6666-4666-8666-666666666666",
      "name": "Email - powiadom zespół sprzedaży",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        520,
        -140
      ],
      "credentials": {
        "smtp": {
          "id": "REPLACE_WITH_SMTP_CREDENTIAL_ID",
          "name": "REPLACE_WITH_SMTP_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"success\":true,\"status\":\"accepted\",\"message\":\"Formularz został przyjęty.\"}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "77777777-7777-4777-8777-777777777777",
      "name": "Odpowiedź Webhook - OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        780,
        -140
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "do weryfikacji",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "timestamp": "={{ $json.timestamp }}",
            "status": "={{ $json.status }}",
            "imie": "={{ $json.imie }}",
            "email": "={{ $json.email }}",
            "telefon": "={{ $json.telefon }}",
            "wiadomosc": "={{ $json.wiadomosc }}",
            "zrodlo_kampanii": "={{ $json.zrodlo_kampanii }}",
            "zgoda_marketingowa": "={{ $json.zgoda_marketingowa }}",
            "validation_errors": "={{ $json.validation_errors }}",
            "raw_payload": "={{ $json.raw_payload }}"
          }
        },
        "options": {}
      },
      "id": "88888888-8888-4888-8888-888888888888",
      "name": "Google Sheets - do weryfikacji",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        0,
        160
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "REPLACE_WITH_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "REPLACE_WITH_FROM_EMAIL@example.com",
        "toEmail": "sales-team@example.com",
        "subject": "=Lead wymaga weryfikacji: {{ $('Normalizuj i waliduj dane').item.json.validation_errors }}",
        "emailFormat": "html",
        "html": "=<h3>Rekord zapisany do zakładki „do weryfikacji”</h3><p><strong>Powód:</strong> {{ $('Normalizuj i waliduj dane').item.json.validation_errors }}</p><ul><li><strong>Imię:</strong> {{ $('Normalizuj i waliduj dane').item.json.imie }}</li><li><strong>Email:</strong> {{ $('Normalizuj i waliduj dane').item.json.email }}</li><li><strong>Telefon:</strong> {{ $('Normalizuj i waliduj dane').item.json.telefon }}</li><li><strong>Źródło kampanii:</strong> {{ $('Normalizuj i waliduj dane').item.json.zrodlo_kampanii }}</li><li><strong>Zgoda marketingowa:</strong> {{ $('Normalizuj i waliduj dane').item.json.zgoda_marketingowa }}</li></ul><p><strong>Wiadomość:</strong><br>{{ $('Normalizuj i waliduj dane').item.json.wiadomosc }}</p>",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "99999999-9999-4999-8999-999999999999",
      "name": "Email - powiadom o weryfikacji",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        260,
        160
      ],
      "credentials": {
        "smtp": {
          "id": "REPLACE_WITH_SMTP_CREDENTIAL_ID",
          "name": "REPLACE_WITH_SMTP_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"success\":false,\"status\":\"needs_verification\",\"message\":\"Formularz został zapisany do weryfikacji.\"}",
        "options": {
          "responseCode": 202
        }
      },
      "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
      "name": "Odpowiedź Webhook - do weryfikacji",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        520,
        160
      ]
    }
  ],
  "connections": {
    "Webhook - formularz lead": {
      "main": [
        [
          {
            "node": "Normalizuj i waliduj dane",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalizuj i waliduj dane": {
      "main": [
        [
          {
            "node": "Czy email i zgoda są obecne?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Czy email i zgoda są obecne?": {
      "main": [
        [
          {
            "node": "Google Sheets - dodaj lub zaktualizuj lead",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Google Sheets - do weryfikacji",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - dodaj lub zaktualizuj lead": {
      "main": [
        [
          {
            "node": "Email - potwierdzenie do klienta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email - potwierdzenie do klienta": {
      "main": [
        [
          {
            "node": "Email - powiadom zespół sprzedaży",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email - powiadom zespół sprzedaży": {
      "main": [
        [
          {
            "node": "Odpowiedź Webhook - OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - do weryfikacji": {
      "main": [
        [
          {
            "node": "Email - powiadom o weryfikacji",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email - powiadom o weryfikacji": {
      "main": [
        [
          {
            "node": "Odpowiedź Webhook - do weryfikacji",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [],
  "triggerCount": 0,
  "active": false,
  "versionId": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
  "meta": {
    "templateCredsSetupCompleted": false
  }
}

2. Przypomnienie o nieopłaconej fakturze

Nieopłacone faktury rzadko są efektem złej woli. Czasem klient przeoczy maila, czasem faktura utknie w administracji, a czasem ktoś po prostu potrzebuje krótkiego przypomnienia z numerem dokumentu i terminem płatności. Dla małej firmy problemem nie jest samo napisanie takiej wiadomości, ale regularność. Trzeba pamiętać, komu przypomnieć, kiedy zrobić to ponownie i kiedy przekazać sprawę do bezpośredniego kontaktu.

Automatyczne przypomnienia pomagają pilnować terminów płatności. Proces krok po kroku z n8n

Problem. W małej firmie płatności często pilnuje właściciel, księgowość albo jedna osoba z administracji. Co kilka dni trzeba sprawdzić faktury, porównać terminy, napisać do klientów i zapisać, kto dostał przypomnienie. To zadanie jest powtarzalne, ale łatwo je odłożyć, zwłaszcza gdy pojawiają się pilniejsze sprawy.

Rozwiązanie. n8n może codziennie rano pobierać listę faktur z arkusza lub systemu księgowego, sprawdzać status płatności i wysyłać przypomnienie tylko wtedy, gdy faktura jest po terminie. Workflow może rozróżniać pierwszy, drugi i trzeci kontakt, żeby wiadomość była dopasowana do sytuacji.

Benefit. Firma szybciej reaguje na opóźnione płatności i poprawia płynność finansową bez ręcznego pilnowania każdego terminu. Komunikacja jest regularna, ale nadal może brzmieć spokojnie i partnersko.

Co robi automatyzacja. Workflow uruchamia się według harmonogramu, pobiera listę faktur, wybiera dokumenty ze statusem „nieopłacona”, sprawdza datę ostatniego przypomnienia i wysyła wiadomość tylko wtedy, gdy minął ustalony odstęp. Po wysyłce aktualizuje arkusz, żeby kolejny mail nie poszedł za szybko.

Przykład w praktyce. Faktura jest 3 dni po terminie. n8n wysyła uprzejme przypomnienie z numerem faktury, kwotą i datą płatności. Jeśli po kolejnych 5 dniach status nadal się nie zmieni, workflow wysyła drugą wiadomość i oznacza sprawę jako wymagającą kontaktu telefonicznego.

Gotowa automatyzacja – Przypomnienia faktur

Ta automatyzacja n8n uruchamia się codziennie o 8:30, pobiera faktury z Google Sheets, wybiera faktury ze statusem „nieopłacona”, których termin płatności jest starszy niż dzisiaj, sprawdza czy ostatnie przypomnienie nie było wysłane w ciągu ostatnich 3 dni, wysyła email do klienta i aktualizuje datę ostatniego przypomnienia w arkuszu.

Co trzeba zmienić, żeby to działało?

  • Zmień PLACEHOLDER_SPREADSHEET_ID na ID swojego arkusza Google Sheets.
  • Zmień Faktury na nazwę zakładki w arkuszu, jeśli używasz innej.
  • Podmień placeholdery credentiali Google Sheets: PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID i PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME.
  • Podmień placeholdery credentiali SMTP: PLACEHOLDER_SMTP_CREDENTIAL_ID i PLACEHOLDER_SMTP_CREDENTIAL_NAME.
  • Zmień adres nadawcy w polu fromEmail: obecnie ustawione jest Finanse <finanse@example.com>.
  • Upewnij się, że arkusz ma dokładnie takie kolumny: numer_faktury, email_klienta, kwota, termin_platnosci, status, data_ostatniego_przypomnienia.
  • Daty mogą być w formacie YYYY-MM-DD albo DD.MM.YYYY.

Jak zainstalować?

  1. Wejdź do n8n.
  2. Otwórz Workflows.
  3. Wklej JSON z sekcji poniżej lub wybierz opcję importu workflow z JSON.
  4. Po imporcie otwórz node Google Sheets i wybierz właściwe credentials oraz arkusz.
  5. Otwórz node Send Email i wybierz właściwe credentials SMTP.
  6. Przetestuj workflow ręcznie.
  7. Aktywuj workflow.

Kod automatyzacji n8n

{
  "name": "Przypomnienia o nieopłaconych fakturach",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 8 * * *"
            }
          ]
        }
      },
      "id": "b2b97fc1-5b9f-4c60-9eb7-3ec1b4f26d01",
      "name": "Codziennie o 8:30",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Faktury",
          "mode": "name"
        },
        "options": {}
      },
      "id": "baf1e0b3-70e8-4e39-b01f-fd225d53a62a",
      "name": "Pobierz faktury z Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        500,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const TIMEZONE = 'Europe/Warsaw';\n\nfunction todayIsoInTimezone(timeZone) {\n  return new Intl.DateTimeFormat('en-CA', {\n    timeZone,\n    year: 'numeric',\n    month: '2-digit',\n    day: '2-digit',\n  }).format(new Date());\n}\n\nfunction parseDate(value) {\n  if (!value) return null;\n\n  const raw = String(value).trim();\n  if (!raw) return null;\n\n  // YYYY-MM-DD\n  let match = raw.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\n  if (match) {\n    return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));\n  }\n\n  // DD.MM.YYYY, DD-MM-YYYY albo DD/MM/YYYY\n  match = raw.match(/^(\\d{1,2})[.\\-/](\\d{1,2})[.\\-/](\\d{4})$/);\n  if (match) {\n    return new Date(Number(match[3]), Number(match[2]) - 1, Number(match[1]));\n  }\n\n  const parsed = new Date(raw);\n  return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction startOfDay(date) {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction daysBetween(laterDate, earlierDate) {\n  const msInDay = 24 * 60 * 60 * 1000;\n  return Math.floor((startOfDay(laterDate) - startOfDay(earlierDate)) / msInDay);\n}\n\nconst todayIso = todayIsoInTimezone(TIMEZONE);\nconst today = parseDate(todayIso);\n\nreturn items\n  .filter((item) => {\n    const row = item.json;\n    const status = String(row.status || '').trim().toLowerCase();\n    const dueDate = parseDate(row.termin_platnosci);\n    const lastReminderDate = parseDate(row.data_ostatniego_przypomnienia);\n\n    const isUnpaid = status === 'nieopłacona';\n    const isOverdue = dueDate && startOfDay(dueDate) < startOfDay(today);\n    const canSendReminder = !lastReminderDate || daysBetween(today, lastReminderDate) >= 3;\n\n    return isUnpaid && isOverdue && canSendReminder;\n  })\n  .map((item) => {\n    const row = item.json;\n    const dueDate = parseDate(row.termin_platnosci);\n\n    return {\n      json: {\n        ...row,\n        data_ostatniego_przypomnienia: todayIso,\n        dni_po_terminie: dueDate ? daysBetween(today, dueDate) : null\n      }\n    };\n  });"
      },
      "id": "f8cfcd72-89f5-47c3-a681-60eb7d5b2930",
      "name": "Filtruj faktury do przypomnienia",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        760,
        300
      ]
    },
    {
      "parameters": {
        "fromEmail": "Finanse <finanse@example.com>",
        "toEmail": "={{ $json.email_klienta }}",
        "subject": "={{ 'Przypomnienie o płatności faktury ' + $json.numer_faktury }}",
        "emailFormat": "html",
        "html": "=<p>Dzień dobry,</p>\n\n<p>Przypominamy o zaległej płatności za fakturę <strong>{{ $json.numer_faktury }}</strong>.</p>\n\n<p><strong>Kwota:</strong> {{ $json.kwota }}<br>\n<strong>Termin płatności:</strong> {{ $json.termin_platnosci }}<br>\n<strong>Liczba dni po terminie:</strong> {{ $json.dni_po_terminie }}</p>\n\n<p>Prosimy o uregulowanie płatności lub kontakt, jeśli płatność została już wykonana.</p>\n\n<p>Pozdrawiamy,<br>Dział finansowy</p>",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "95e3842b-dc36-45eb-9d3b-87f272bc5b48",
      "name": "Wyślij przypomnienie email",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1020,
        300
      ],
      "credentials": {
        "smtp": {
          "id": "PLACEHOLDER_SMTP_CREDENTIAL_ID",
          "name": "PLACEHOLDER_SMTP_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Faktury",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "numer_faktury": "={{ $('Filtruj faktury do przypomnienia').item.json.numer_faktury }}",
            "data_ostatniego_przypomnienia": "={{ $('Filtruj faktury do przypomnienia').item.json.data_ostatniego_przypomnienia }}"
          },
          "matchingColumns": [
            "numer_faktury"
          ],
          "schema": [
            {
              "id": "numer_faktury",
              "displayName": "numer_faktury",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "email_klienta",
              "displayName": "email_klienta",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "kwota",
              "displayName": "kwota",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "termin_platnosci",
              "displayName": "termin_platnosci",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "data_ostatniego_przypomnienia",
              "displayName": "data_ostatniego_przypomnienia",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "c8a09e9d-384e-4ff8-94f8-0b65b8a62836",
      "name": "Zaktualizuj datę przypomnienia",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        1280,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Codziennie o 8:30": {
      "main": [
        [
          {
            "node": "Pobierz faktury z Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pobierz faktury z Google Sheets": {
      "main": [
        [
          {
            "node": "Filtruj faktury do przypomnienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtruj faktury do przypomnienia": {
      "main": [
        [
          {
            "node": "Wyślij przypomnienie email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wyślij przypomnienie email": {
      "main": [
        [
          {
            "node": "Zaktualizuj datę przypomnienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Warsaw"
  },
  "versionId": "PLACEHOLDER_VERSION_ID",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "id": "PLACEHOLDER_WORKFLOW_ID",
  "tags": []
}

3. Alert, gdy strona lub formularz nie działa

Strona firmowa może działać, a mimo to nie dowozić najważniejszej funkcji. Formularz kontaktowy nie wysyła wiadomości, przycisk prowadzi do błędu, podstrona z ofertą ładuje się zbyt długo albo zwraca kod, którego użytkownik nie widzi, ale system już powinien potraktować go jako problem. W małej firmie takie awarie często wychodzą dopiero wtedy, gdy ktoś zauważy brak nowych zapytań

Alert, gdy strona lub formularz nie działa. Automatyzacja z n8n

Problem. Strona może wyglądać poprawnie, ale formularz kontaktowy nie wysyła wiadomości. Podstrona z ofertą może zwracać błąd, a nikt nie zauważy tego przez kilka godzin. Dla małej firmy taki przestój oznacza ryzyko utraty zapytań.

Rozwiązanie. n8n może cyklicznie sprawdzać ważne adresy URL, mierzyć czas odpowiedzi i wysyłać alert, gdy strona zwróci błąd. Dodatkowo workflow może wykonać prosty test formularza, na przykład wysłać techniczne zgłoszenie testowe na dedykowany adres. Do kontroli widoczności strony w Google warto równolegle wykorzystać monitoring pozycji strony.

Benefit. O problemie dowiadujesz się szybko, zanim zdąży przełożyć się na większą liczbę utraconych leadów. To prosta automatyzacja, która wspiera sprzedaż i bezpieczeństwo operacyjne strony.

Co robi automatyzacja. Workflow uruchamia się co kilka minut, pobiera listę adresów z arkusza, wykonuje zapytanie HTTP, sprawdza kod statusu i czas odpowiedzi. Jeśli wynik jest nieprawidłowy, wysyła alert z adresem URL, godziną wykrycia, kodem błędu i krótką sugestią pierwszego kroku.

Przykład w praktyce. Formularz kontaktowy przestaje odpowiadać po aktualizacji wtyczki. n8n wykrywa problem podczas testu, wysyła alert do właściciela i osoby technicznej, a wynik zapisuje w arkuszu historii incydentów.

Gotowa automatyzacja – Monitor URL co 10 minut

Ta automatyzacja n8n co 10 minut pobiera listę adresów URL z Google Sheets, sprawdza każdy adres przez HTTP Request, mierzy czas odpowiedzi, zapisuje wynik testu do Google Sheets oraz wysyła alert e-mail i wiadomość na Slack, jeśli kod statusu nie jest z zakresu 200–299 albo czas odpowiedzi przekracza 5 sekund.

Co trzeba zmienić, żeby to działało

  • PLACEHOLDER_GOOGLE_SHEET_ID – podmień na ID arkusza Google Sheets.
  • URLS – zakładka z monitorowanymi adresami. Przygotuj kolumny: url, opcjonalnie label i enabled.
  • LOGS – zakładka na wyniki testów. Przygotuj kolumny: url, label, statusCode, responseTimeMs, checkedAt, statusOk, shouldAlert, result, alertSubject, alertMessage.
  • PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL – wybierz swoje credentiale Google Sheets w n8n.
  • PLACEHOLDER_SMTP_CREDENTIAL – wybierz credentiale SMTP do wysyłki e-maili.
  • alerts@example.invalid i admin@example.invalid – podmień na adres nadawcy i odbiorcy alertów.
  • PLACEHOLDER_SLACK_BOT_TOKEN_CREDENTIAL – wybierz credential typu HTTP Bearer Auth z tokenem bota Slack.
  • PLACEHOLDER_SLACK_CHANNEL_ID – podmień na ID kanału Slack, np. testowego kanału alertów.
  • Próg czasu odpowiedzi jest ustawiony na 5000 ms. Jeśli chcesz inny próg, zmień wartość 5000 w node Build Result and Alert Payload.

Jak zainstalować

  1. Wejdź do n8n.
  2. Utwórz nowy workflow.
  3. Wklej JSON z sekcji poniżej.
  4. Uzupełnij ID arkusza Google Sheets, nazwy zakładek, adresy e-mail i ID kanału Slack.
  5. Uruchom test ręcznie na przykładowym arkuszu z nieprodukcyjnymi adresami URL.
  6. Po poprawnym teście zapisz i aktywuj workflow.

Kod automatyzacji n8n JSON

{
  "name": "URL Monitor - Small Business",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 10
            }
          ]
        }
      },
      "id": "schedule-trigger-10-min",
      "name": "Schedule Trigger - every 10 minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "URLS",
          "mode": "name"
        },
        "options": {}
      },
      "id": "read-urls-google-sheets",
      "name": "Read URLs from Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        240,
        0
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all();\nconst output = [];\n\nfor (const item of rows) {\n  const rawUrl = item.json.url || item.json.URL || item.json.adres_url || item.json.Adres_URL || \"\";\n  const url = String(rawUrl).trim();\n\n  const rawEnabled = item.json.enabled ?? item.json.Enabled ?? item.json.aktywny ?? item.json.Aktywny ?? true;\n  const enabled = String(rawEnabled).toLowerCase();\n\n  if (!url) {\n    continue;\n  }\n\n  if (enabled === \"false\" || enabled === \"0\" || enabled === \"no\" || enabled === \"nie\") {\n    continue;\n  }\n\n  if (!url.startsWith(\"http://\") && !url.startsWith(\"https://\")) {\n    continue;\n  }\n\n  output.push({\n    json: {\n      url,\n      label: item.json.label || item.json.Label || item.json.nazwa || item.json.Nazwa || url\n    }\n  });\n}\n\nreturn output;"
      },
      "id": "normalize-urls",
      "name": "Normalize URLs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "url-field",
              "name": "url",
              "value": "={{ $json.url }}",
              "type": "string"
            },
            {
              "id": "label-field",
              "name": "label",
              "value": "={{ $json.label }}",
              "type": "string"
            },
            {
              "id": "started-at-field",
              "name": "startedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "prepare-timestamp",
      "name": "Prepare Timestamp",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        720,
        0
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "n8n-small-business-url-monitor/1.0"
            },
            {
              "name": "Accept",
              "value": "*/*"
            }
          ]
        },
        "options": {
          "timeout": 10000,
          "redirect": {
            "redirect": {
              "followRedirects": true,
              "maxRedirects": 5
            }
          },
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "text",
              "outputPropertyName": "body"
            }
          }
        }
      },
      "id": "http-request-check-url",
      "name": "HTTP Request - Check URL",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        960,
        0
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "url-result-field",
              "name": "url",
              "value": "={{ $node[\"Prepare Timestamp\"].json.url }}",
              "type": "string"
            },
            {
              "id": "label-result-field",
              "name": "label",
              "value": "={{ $node[\"Prepare Timestamp\"].json.label }}",
              "type": "string"
            },
            {
              "id": "status-code-field",
              "name": "statusCode",
              "value": "={{ $json.statusCode || 0 }}",
              "type": "number"
            },
            {
              "id": "response-time-field",
              "name": "responseTimeMs",
              "value": "={{ Date.now() - new Date($node[\"Prepare Timestamp\"].json.startedAt).getTime() }}",
              "type": "number"
            },
            {
              "id": "checked-at-field",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            },
            {
              "id": "status-ok-field",
              "name": "statusOk",
              "value": "={{ String($json.statusCode || 0).match(/^2\\d\\d$/) !== null }}",
              "type": "boolean"
            },
            {
              "id": "should-alert-field",
              "name": "shouldAlert",
              "value": "={{ String($json.statusCode || 0).match(/^2\\d\\d$/) === null || (Date.now() - new Date($node[\"Prepare Timestamp\"].json.startedAt).getTime()) > 5000 }}",
              "type": "boolean"
            },
            {
              "id": "result-field",
              "name": "result",
              "value": "={{ String($json.statusCode || 0).match(/^2\\d\\d$/) !== null && !((Date.now() - new Date($node[\"Prepare Timestamp\"].json.startedAt).getTime()) > 5000) ? \"OK\" : \"ALERT\" }}",
              "type": "string"
            },
            {
              "id": "alert-subject-field",
              "name": "alertSubject",
              "value": "={{ \"URL monitor alert: \" + $node[\"Prepare Timestamp\"].json.url }}",
              "type": "string"
            },
            {
              "id": "alert-message-field",
              "name": "alertMessage",
              "value": "={{ \"Alert monitoringu URL\\n\\nURL: \" + $node[\"Prepare Timestamp\"].json.url + \"\\nEtykieta: \" + $node[\"Prepare Timestamp\"].json.label + \"\\nKod statusu: \" + ($json.statusCode || 0) + \"\\nCzas odpowiedzi: \" + (Date.now() - new Date($node[\"Prepare Timestamp\"].json.startedAt).getTime()) + \" ms\\nData: \" + new Date().toISOString() + \"\\nPowód: \" + (String($json.statusCode || 0).match(/^2\\d\\d$/) === null ? \"status poza zakresem 200-299\" : \"czas odpowiedzi powyżej 5 sekund\") }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "build-result",
      "name": "Build Result and Alert Payload",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1200,
        0
      ]
    },
    {
      "parameters": {
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "LOGS",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "append-result-google-sheets",
      "name": "Append Result to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1440,
        -120
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "condition-should-alert-true",
              "leftValue": "={{ $json.shouldAlert }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-alert-needed",
      "name": "IF Alert Needed",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1440,
        120
      ]
    },
    {
      "parameters": {
        "fromEmail": "alerts@example.invalid",
        "toEmail": "admin@example.invalid",
        "subject": "={{ $json.alertSubject }}",
        "emailFormat": "text",
        "text": "={{ $json.alertMessage }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "send-alert-email",
      "name": "Send Alert Email",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1680,
        40
      ],
      "credentials": {
        "smtp": {
          "id": "PLACEHOLDER_SMTP_CREDENTIAL_ID",
          "name": "PLACEHOLDER_SMTP_CREDENTIAL"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://slack.com/api/chat.postMessage",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json; charset=utf-8"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ { \"channel\": \"PLACEHOLDER_SLACK_CHANNEL_ID\", \"text\": $json.alertMessage } }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "send-slack-alert",
      "name": "Send Slack Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1680,
        200
      ],
      "credentials": {
        "httpBearerAuth": {
          "id": "PLACEHOLDER_SLACK_BOT_TOKEN_CREDENTIAL_ID",
          "name": "PLACEHOLDER_SLACK_BOT_TOKEN_CREDENTIAL"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger - every 10 minutes": {
      "main": [
        [
          {
            "node": "Read URLs from Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read URLs from Google Sheets": {
      "main": [
        [
          {
            "node": "Normalize URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize URLs": {
      "main": [
        [
          {
            "node": "Prepare Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Timestamp": {
      "main": [
        [
          {
            "node": "HTTP Request - Check URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request - Check URL": {
      "main": [
        [
          {
            "node": "Build Result and Alert Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Result and Alert Payload": {
      "main": [
        [
          {
            "node": "Append Result to Google Sheets",
            "type": "main",
            "index": 0
          },
          {
            "node": "IF Alert Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Alert Needed": {
      "main": [
        [
          {
            "node": "Send Alert Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Warsaw"
  },
  "versionId": "PLACEHOLDER_VERSION_ID",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

4. Cotygodniowy raport sprzedaży i marketingu

Problem. Dane są rozproszone. Leady są w arkuszu, zamówienia w sklepie, kampanie w panelach reklamowych, a część informacji nadal pojawia się w mailach. Gdy przychodzi poniedziałek, ktoś musi zebrać wszystko ręcznie i przygotować podsumowanie.

Rozwiązanie. n8n raz w tygodniu pobiera dane z wybranych źródeł, liczy podstawowe wskaźniki i wysyła raport do właściciela firmy. Taki raport może zawierać liczbę leadów, liczbę zamówień, wartość sprzedaży, najskuteczniejsze źródła kontaktu i krótką listę tematów do sprawdzenia.

Benefit. Właściciel widzi najważniejsze liczby bez proszenia zespołu o ręczne zestawienia. Decyzje są szybsze, bo raport pojawia się regularnie i ma zawsze tę samą strukturę.

Co robi automatyzacja. Workflow uruchamia się w poniedziałek rano, pobiera dane z arkuszy, sklepu i kampanii, filtruje poprzedni tydzień, liczy wyniki i wysyła czytelne podsumowanie. Można dodać też krok z AI, który zamieni surowe liczby w krótkie rekomendacje na kolejny tydzień.

Przykład w praktyce. W każdy poniedziałek o 9:00 właściciel dostaje mail: „W poprzednim tygodniu pojawiło się 27 leadów, 9 zamówień i 3 zapytania z formularza wyceny. Najlepsze źródło: organiczne wejścia z Google. Do sprawdzenia: niski współczynnik odpowiedzi na zapytania z piątku”.

Gotowa automatyzacja – Cotygodniowy raport sprzedaży i marketingu

Automatyzacja w n8n uruchamia się w każdy poniedziałek o 9:00 i przygotowuje cotygodniowy raport dla małej firmy na podstawie trzech arkuszy Google Sheets: leady, zamówienia i kampanie. Raport liczy liczbę leadów, liczbę zamówień, wartość sprzedaży, najlepsze źródło leadów oraz porównanie z poprzednim tygodniem. Następnie wysyła podsumowanie e-mailem i na Slacka.

Co trzeba zmienić, żeby to działało?

  • Podmień PLACEHOLDER_GOOGLE_SHEET_ID na ID pliku Google Sheets.
  • Upewnij się, że arkusz ma zakładki: leady, zamówienia, kampanie.
  • W zakładce leady dodaj kolumnę z datą, np. data, oraz źródłem, np. źródło, source albo utm_source.
  • W zakładce zamówienia dodaj kolumnę z datą oraz wartością zamówienia, np. wartość, kwota, amount, total albo revenue.
  • W zakładce kampanie dodaj kolumnę z datą i opcjonalnie kosztem, np. koszt, spend, cost albo budget.
  • Po imporcie workflow w n8n ustaw credentiale dla Google Sheets, SMTP / Send Email oraz Slack.
  • Podmień adres owner@example.com, adres nadawcy raporty@example.com oraz PLACEHOLDER_SLACK_CHANNEL_ID.
  • Opcjonalny node AI jest placeholderem. Możesz go zastąpić node’em OpenAI / AI Agent, który zwróci pole aiRecommendations jako tablicę trzech rekomendacji.

Jak zainstalować?

  1. Skopiuj JSON z bloku kodu poniżej.
  2. Wejdź do n8n.
  3. Wybierz Workflows → Import from clipboard albo Import from file.
  4. Wklej JSON i zaimportuj workflow.
  5. Otwórz node’y Google Sheets, Email i Slack, a następnie wybierz właściwe credentiale.
  6. Uruchom testowo workflow ręcznie.
  7. Jeżeli raport wygląda poprawnie, aktywuj workflow.
{
  "name": "Cotygodniowy raport małej firmy",
  "nodes": [
    {
      "parameters": {
        "content": "## Konfiguracja\n1. Podmień PLACEHOLDER_GOOGLE_SHEET_ID na ID arkusza Google.\n2. Arkusze powinny mieć zakładki: leady, zamówienia, kampanie.\n3. Ustaw credentiale: Google Sheets, SMTP/Send Email oraz Slack.\n4. Opcjonalnie zastąp node AI placeholder node'em OpenAI / AI Agent.",
        "height": 180,
        "width": 420
      },
      "id": "baf395df-d4db-4d44-89aa-f153ecc51759",
      "name": "Instrukcja konfiguracji",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -620,
        -260
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "id": "2ba745f5-3e8e-4d37-bfaa-51f1e6a4db6f",
      "name": "Poniedzialek 09:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -620,
        40
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "leady",
          "mode": "name"
        },
        "options": {
          "range": "A:Z"
        }
      },
      "id": "f0e9c0f6-4ad0-4bd0-85cc-9efc3383c7a2",
      "name": "Pobierz leady",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        -380,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { leady: $input.all().map(item => item.json) } }];"
      },
      "id": "c3f7b604-67a4-40ed-8d32-0a49e8a88f20",
      "name": "Zapisz leady",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -160,
        40
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "zamówienia",
          "mode": "name"
        },
        "options": {
          "range": "A:Z"
        }
      },
      "id": "ed3a3477-0ff4-4e63-8c22-e6c91ae4a23c",
      "name": "Pobierz zamowienia",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        60,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { zamowienia: $input.all().map(item => item.json) } }];"
      },
      "id": "928f4ef3-fd11-41a2-96eb-e89c6f7d8b6b",
      "name": "Zapisz zamowienia",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        280,
        40
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "documentId": {
          "__rl": true,
          "value": "PLACEHOLDER_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "kampanie",
          "mode": "name"
        },
        "options": {
          "range": "A:Z"
        }
      },
      "id": "d7c0cf5a-d512-4e14-a49a-d78d41e4539b",
      "name": "Pobierz kampanie",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        500,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "PLACEHOLDER_GOOGLE_SHEETS_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const leadRows = ($('Zapisz leady').first().json.leady || []);\nconst orderRows = ($('Zapisz zamowienia').first().json.zamowienia || []);\nconst campaignRows = $input.all().map(item => item.json);\n\nconst dateKeys = ['data', 'date', 'created_at', 'created', 'utworzono', 'data_utworzenia', 'data dodania', 'timestamp'];\nconst sourceKeys = ['zrodlo', 'źródło', 'source', 'lead_source', 'kanal', 'kanał', 'medium', 'utm_source'];\nconst orderValueKeys = ['wartosc', 'wartość', 'kwota', 'amount', 'value', 'total', 'suma', 'przychod', 'przychód', 'revenue'];\nconst statusKeys = ['status', 'stan'];\nconst campaignSpendKeys = ['koszt', 'spend', 'wydatki', 'cost', 'budget', 'budzet', 'budżet'];\n\nfunction normalizeKey(key) {\n  return String(key || '')\n    .toLowerCase()\n    .normalize('NFD')\n    .replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/ł/g, 'l')\n    .replace(/\\s+/g, '_')\n    .replace(/[^\\w]/g, '');\n}\n\nfunction getValue(row, aliases) {\n  const map = {};\n  for (const key of Object.keys(row || {})) {\n    map[normalizeKey(key)] = key;\n  }\n  for (const alias of aliases) {\n    const originalKey = map[normalizeKey(alias)];\n    if (originalKey !== undefined && row[originalKey] !== undefined && row[originalKey] !== '') {\n      return row[originalKey];\n    }\n  }\n  return undefined;\n}\n\nfunction parseDate(value) {\n  if (!value) return null;\n  if (value instanceof Date) return value;\n\n  if (typeof value === 'number') {\n    const excelEpoch = new Date(Date.UTC(1899, 11, 30));\n    return new Date(excelEpoch.getTime() + value * 24 * 60 * 60 * 1000);\n  }\n\n  const s = String(value).trim();\n  const parsed = Date.parse(s);\n  if (!Number.isNaN(parsed)) return new Date(parsed);\n\n  const m = s.match(/^(\\d{1,2})[.\\-/](\\d{1,2})[.\\-/](\\d{2,4})(?:\\s+(\\d{1,2}):(\\d{2}))?/);\n  if (m) {\n    const day = Number(m[1]);\n    const month = Number(m[2]) - 1;\n    const year = Number(m[3].length === 2 ? `20${m[3]}` : m[3]);\n    const hour = Number(m[4] || 0);\n    const minute = Number(m[5] || 0);\n    return new Date(year, month, day, hour, minute);\n  }\n\n  return null;\n}\n\nfunction parseNumber(value) {\n  if (typeof value === 'number') return value;\n  if (value === undefined || value === null || value === '') return 0;\n\n  const cleaned = String(value)\n    .replace(/\\s/g, '')\n    .replace(/PLN/gi, '')\n    .replace(/zł/gi, '')\n    .replace(',', '.')\n    .replace(/[^\\d.-]/g, '');\n\n  const n = Number(cleaned);\n  return Number.isFinite(n) ? n : 0;\n}\n\nfunction startOfWeekMonday(date) {\n  const d = new Date(date);\n  d.setHours(0, 0, 0, 0);\n  const day = d.getDay();\n  const diff = day === 0 ? -6 : 1 - day;\n  d.setDate(d.getDate() + diff);\n  return d;\n}\n\nfunction addDays(date, days) {\n  const d = new Date(date);\n  d.setDate(d.getDate() + days);\n  return d;\n}\n\nfunction formatDate(date) {\n  return date.toISOString().slice(0, 10);\n}\n\nfunction inRange(row, start, end) {\n  const d = parseDate(getValue(row, dateKeys));\n  return d !== null && d >= start && d < end;\n}\n\nfunction isCancelled(row) {\n  const status = String(getValue(row, statusKeys) || '').toLowerCase();\n  return ['cancelled', 'canceled', 'anulowane', 'anulowano', 'zwrot', 'refund'].some(word => status.includes(word));\n}\n\nfunction topSource(rows) {\n  const counts = {};\n  for (const row of rows) {\n    const source = String(getValue(row, sourceKeys) || 'nieznane').trim() || 'nieznane';\n    counts[source] = (counts[source] || 0) + 1;\n  }\n  const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);\n  return sorted.length ? { source: sorted[0][0], leads: sorted[0][1], breakdown: counts } : { source: 'brak danych', leads: 0, breakdown: {} };\n}\n\nfunction compare(current, previous) {\n  const diff = current - previous;\n  const percent = previous === 0 ? (current === 0 ? 0 : null) : (diff / previous) * 100;\n  return { current, previous, diff, percent };\n}\n\nconst now = new Date();\nconst startThisWeek = startOfWeekMonday(now);\nconst startReportWeek = addDays(startThisWeek, -7);\nconst startPreviousWeek = addDays(startThisWeek, -14);\nconst endReportWeek = startThisWeek;\n\nconst leadsReportWeek = leadRows.filter(row => inRange(row, startReportWeek, endReportWeek));\nconst leadsPreviousWeek = leadRows.filter(row => inRange(row, startPreviousWeek, startReportWeek));\n\nconst ordersReportWeek = orderRows.filter(row => inRange(row, startReportWeek, endReportWeek) && !isCancelled(row));\nconst ordersPreviousWeek = orderRows.filter(row => inRange(row, startPreviousWeek, startReportWeek) && !isCancelled(row));\n\nconst campaignsReportWeek = campaignRows.filter(row => inRange(row, startReportWeek, endReportWeek));\nconst campaignsPreviousWeek = campaignRows.filter(row => inRange(row, startPreviousWeek, startReportWeek));\n\nconst salesReportWeek = ordersReportWeek.reduce((sum, row) => sum + parseNumber(getValue(row, orderValueKeys)), 0);\nconst salesPreviousWeek = ordersPreviousWeek.reduce((sum, row) => sum + parseNumber(getValue(row, orderValueKeys)), 0);\n\nconst campaignSpendReportWeek = campaignsReportWeek.reduce((sum, row) => sum + parseNumber(getValue(row, campaignSpendKeys)), 0);\nconst campaignSpendPreviousWeek = campaignsPreviousWeek.reduce((sum, row) => sum + parseNumber(getValue(row, campaignSpendKeys)), 0);\n\nreturn [\n  {\n    json: {\n      generatedAt: new Date().toISOString(),\n      periodStart: formatDate(startReportWeek),\n      periodEndExclusive: formatDate(endReportWeek),\n      previousPeriodStart: formatDate(startPreviousWeek),\n      previousPeriodEndExclusive: formatDate(startReportWeek),\n      periodLabel: `${formatDate(startReportWeek)} - ${formatDate(addDays(endReportWeek, -1))}`,\n      previousPeriodLabel: `${formatDate(startPreviousWeek)} - ${formatDate(addDays(startReportWeek, -1))}`,\n      leads: compare(leadsReportWeek.length, leadsPreviousWeek.length),\n      orders: compare(ordersReportWeek.length, ordersPreviousWeek.length),\n      salesValue: compare(salesReportWeek, salesPreviousWeek),\n      campaigns: compare(campaignsReportWeek.length, campaignsPreviousWeek.length),\n      campaignSpend: compare(campaignSpendReportWeek, campaignSpendPreviousWeek),\n      topLeadSource: topSource(leadsReportWeek),\n      rawRowCounts: {\n        leadRows: leadRows.length,\n        orderRows: orderRows.length,\n        campaignRows: campaignRows.length\n      }\n    }\n  }\n];"
      },
      "id": "1c519188-10f5-48a2-b7e7-a7a653a94f65",
      "name": "Oblicz raport tygodniowy",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        740,
        40
      ]
    },
    {
      "parameters": {
        "content": "## Opcjonalny AI node\nZastąp następny node właściwym node'em OpenAI / AI Agent, jeżeli chcesz generować rekomendacje przez model AI. Zachowaj output: aiRecommendations jako tablica 3 tekstów.",
        "height": 140,
        "width": 360
      },
      "id": "1ceda6de-7760-4ad7-80bd-9c4092fd1c18",
      "name": "Notatka AI",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        700,
        -180
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\n// PLACEHOLDER AI:\n// Możesz zastąpić ten node node'em OpenAI / AI Agent.\n// Wejście dla AI: cały obiekt JSON z metrykami tygodnia.\n// Oczekiwany output: { \"aiRecommendations\": [\"...\", \"...\", \"...\"] }\n\nconst recommendations = [\n  `Wzmocnij źródło \"${data.topLeadSource.source}\", bo wygenerowało najwięcej leadów w raportowanym tygodniu.`,\n  data.leads.diff < 0\n    ? 'Sprawdź spadek liczby leadów i porównaj zmiany w kampaniach, formularzach oraz budżetach.'\n    : 'Utrzymaj działania lead generation i przetestuj jedną dodatkową kreację lub landing page.',\n  data.salesValue.diff < 0\n    ? 'Przejrzyj zamówienia utracone lub anulowane i skontaktuj się z leadami o najwyższej intencji zakupu.'\n    : 'Wykorzystaj wzrost sprzedaży do przygotowania krótkiej oferty follow-up dla nowych klientów.'\n];\n\nreturn [{ json: { ...data, aiRecommendations: recommendations } }];"
      },
      "id": "2de8a2d2-cb24-47c6-aac5-d4d52eb4dbb8",
      "name": "Opcjonalny AI - rekomendacje placeholder",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        980,
        40
      ],
      "notes": "Zastąp ten node node'em OpenAI / AI Agent, jeśli chcesz prawdziwe rekomendacje AI.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "const r = $input.first().json;\n\nfunction fmtNumber(value) {\n  return new Intl.NumberFormat('pl-PL', { maximumFractionDigits: 0 }).format(value || 0);\n}\n\nfunction fmtMoney(value) {\n  return new Intl.NumberFormat('pl-PL', { style: 'currency', currency: 'PLN', maximumFractionDigits: 0 }).format(value || 0);\n}\n\nfunction fmtChange(metric, formatter = fmtNumber) {\n  const sign = metric.diff > 0 ? '+' : '';\n  const percent = metric.percent === null ? 'brak bazy porównawczej' : `${sign}${metric.percent.toFixed(1).replace('.', ',')}%`;\n  return `${formatter(metric.current)} (${sign}${formatter(metric.diff)} vs poprzedni tydzień, ${percent})`;\n}\n\nconst recs = (r.aiRecommendations || [])\n  .slice(0, 3)\n  .map((item, index) => `${index + 1}. ${item}`)\n  .join('\\n');\n\nconst subject = `Cotygodniowy raport firmy: ${r.periodLabel}`;\n\nconst emailText = `Cześć,\n\noto cotygodniowy raport firmy za okres ${r.periodLabel}.\n\nNajważniejsze wyniki:\n- Liczba leadów: ${fmtChange(r.leads)}\n- Liczba zamówień: ${fmtChange(r.orders)}\n- Wartość sprzedaży: ${fmtChange(r.salesValue, fmtMoney)}\n- Najskuteczniejsze źródło leadów: ${r.topLeadSource.source} (${r.topLeadSource.leads} leadów)\n- Kampanie w arkuszu: ${fmtChange(r.campaigns)}\n- Koszt kampanii: ${fmtChange(r.campaignSpend, fmtMoney)}\n\nPorównanie dotyczy poprzedniego okresu: ${r.previousPeriodLabel}.\n\nRekomendacje na kolejny tydzień:\n${recs || 'Brak rekomendacji. Włącz opcjonalny node AI albo uzupełnij logikę rekomendacji.'}\n\nPozdrawiam,\nAutomatyczny raport n8n`;\n\nconst slackText = `*Cotygodniowy raport firmy* (${r.periodLabel})\n\n• Leady: ${fmtChange(r.leads)}\n• Zamówienia: ${fmtChange(r.orders)}\n• Sprzedaż: ${fmtChange(r.salesValue, fmtMoney)}\n• Najlepsze źródło leadów: *${r.topLeadSource.source}* (${r.topLeadSource.leads})\n• Kampanie: ${fmtChange(r.campaigns)}\n• Koszt kampanii: ${fmtChange(r.campaignSpend, fmtMoney)}\n\n*Rekomendacje:*\n${recs || 'Brak rekomendacji.'}`;\n\nreturn [{ json: { ...r, subject, emailText, slackText } }];"
      },
      "id": "5e1c3a6a-8426-4873-bd57-fd11c6b83160",
      "name": "Przygotuj podsumowanie tekstowe",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1220,
        40
      ]
    },
    {
      "parameters": {
        "fromEmail": "raporty@example.com",
        "toEmail": "owner@example.com",
        "subject": "={{ $json.subject }}",
        "emailFormat": "text",
        "text": "={{ $json.emailText }}",
        "options": {}
      },
      "id": "9f4ad658-6165-4d24-8a67-bca48dc436c8",
      "name": "Wyslij e-mail do wlasciciela",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1460,
        -60
      ],
      "credentials": {
        "smtp": {
          "id": "PLACEHOLDER_SMTP_CREDENTIAL_ID",
          "name": "PLACEHOLDER_SMTP_CREDENTIAL_NAME"
        }
      }
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "PLACEHOLDER_SLACK_CHANNEL_ID",
          "mode": "id"
        },
        "text": "={{ $json.slackText }}",
        "otherOptions": {}
      },
      "id": "754aa1cb-e35d-4e89-8f45-62968b4482fe",
      "name": "Wyslij na Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        1460,
        140
      ],
      "credentials": {
        "slackOAuth2Api": {
          "id": "PLACEHOLDER_SLACK_CREDENTIAL_ID",
          "name": "PLACEHOLDER_SLACK_CREDENTIAL_NAME"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Poniedzialek 09:00": {
      "main": [
        [
          {
            "node": "Pobierz leady",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pobierz leady": {
      "main": [
        [
          {
            "node": "Zapisz leady",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zapisz leady": {
      "main": [
        [
          {
            "node": "Pobierz zamowienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pobierz zamowienia": {
      "main": [
        [
          {
            "node": "Zapisz zamowienia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zapisz zamowienia": {
      "main": [
        [
          {
            "node": "Pobierz kampanie",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pobierz kampanie": {
      "main": [
        [
          {
            "node": "Oblicz raport tygodniowy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Oblicz raport tygodniowy": {
      "main": [
        [
          {
            "node": "Opcjonalny AI - rekomendacje placeholder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Opcjonalny AI - rekomendacje placeholder": {
      "main": [
        [
          {
            "node": "Przygotuj podsumowanie tekstowe",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Przygotuj podsumowanie tekstowe": {
      "main": [
        [
          {
            "node": "Wyslij e-mail do wlasciciela",
            "type": "main",
            "index": 0
          },
          {
            "node": "Wyslij na Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Warsaw"
  },
  "versionId": "c25c3b08-5f8a-468b-b90a-c8c062751bb4",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "id": "PLACEHOLDER_WORKFLOW_ID",
  "tags": []
}

Potrzebujesz wsparcia w automatyzacji? Znajdź eksperta na _partners

n8n daje dużą elastyczność, ale nie każda firma ma czas, żeby samodzielnie projektować workflow, testować integracje i pilnować wyjątków. Jeśli wiesz, że automatyzacja pomogłaby Ci uporządkować sprzedaż, obsługę klienta, faktury albo procesy operacyjne, ale nie chcesz wdrażać jej samodzielnie, możesz skorzystać ze wsparcia specjalistów.

Na platformie _partners znajdziesz ekspertów, którzy pomagają firmom w usługach cyfrowych, wdrożeniach i automatyzacji procesów. To dobre rozwiązanie, gdy chcesz przejść od pomysłu do działającego workflow szybciej. Partner może pomóc dobrać narzędzia, zaprojektować logikę automatyzacji, połączyć systemy i przygotować proces tak, żeby był bezpieczny, czytelny i łatwy do rozwijania.

Buduj przewagę i zacznij automatyzować

Automatyzacja w małej firmie nie musi zaczynać się od wielkiego projektu. Najlepiej zacząć od procesów, które powtarzasz najczęściej i które mają bezpośredni wpływ na sprzedaż, obsługę klienta albo płynność pracy zespołu.

Lead z formularza, przypomnienie o fakturze, obsługa zamówienia, alert techniczny i cotygodniowy raport to pięć dobrych punktów startu. Każdy z tych workflow porządkuje konkretny fragment firmy i daje szybki efekt: mniej ręcznego klikania, mniej pomyłek i szybszą reakcję na ważne zdarzenia.

Jeśli chcesz testować takie automatyzacje we własnym środowisku, zobacz VPS z n8n. To dobry wybór, gdy zależy Ci na kontroli nad procesami, danymi i możliwością dalszej rozbudowy automatyzacji.

FAQ – automatyzacje n8n w małej firmie

Tak, zwłaszcza gdy firma korzysta z kilku narzędzi i regularnie wykonuje powtarzalne czynności. n8n pozwala połączyć formularze, arkusze, CRM, sklep, email i komunikatory w jeden workflow.

Najczęściej warto zacząć od obsługi leadów z formularza. To prosty proces, który szybko skraca czas reakcji na zapytanie klienta i porządkuje dane sprzedażowe.

Tak. n8n obsługuje import i eksport workflow jako JSON. Przed udostępnieniem kodu trzeba usunąć dane testowe, sekrety i wrażliwe nazwy credentiali.

Nie. Wiele procesów można zbudować z gotowych node’ów. Kod przydaje się dopiero przy bardziej niestandardowych transformacjach danych lub zaawansowanej logice.

Możesz skorzystać z własnego środowiska, na przykład VPS z preinstalowanym n8n. To dobre rozwiązanie, gdy chcesz mieć kontrolę nad danymi i stabilnością automatyzacji.

Łukasz Bielawski
>
Łukasz Bielawski
Zawsze chętny do pomocy. Od 17 lat zajmuje się marketingiem internetowym, z naciskiem na działania seo oraz kampanie w ekosystemie Google Ads. Prywatnie pasjonat motoryzacji.

Dodaj komentarz

Twój adres e-mail nie będzie opublikowany.

Szukasz dalej?