Przejdź do głównej zawartości

Gotowy workflow: formularz, arkusz i powiadomienie

Gotowy workflow · po Module 5Czas czytania: ~13 minWdrożenie: ~35 min (z konfiguracją OAuth Google Sheets)

Zgłoszenia z formularza - zapisy na webinar, leady, ankiety - rozjeżdżają się po skrzynce mailowej i giną, aż ktoś przypomni sobie, że trzeba było na nie odpowiedzieć. Ten gotowiec zamyka je w jednym miejscu: arkuszu Google, z natychmiastowym powiadomieniem na Telegramie. Zamiast osobnego narzędzia formularzowego (Typeform, Google Forms, JotForm) korzystasz z node'a, który n8n ma już wbudowany - n8n Form Trigger (dokumentacja) sam hostuje formularz pod adresem Twojej instancji, bez zakładania konta w kolejnym serwisie.

To konkretny wariant przepisu "Lead z formularza → CRM → powiadomienie" z Modułu 8: Wzorce i wdrożenia - tu zamiast CRM masz najprostszy możliwy magazyn danych, arkusz Google, a walidację i normalizację danych ćwiczysz w praktyce w node'cie Code z Modułu 5: Kod i sub-workflow, zamiast tylko czytać o nim w teorii.

  • Moduł 5 ukończony - wystarczy znajomość Code Node (tryby uruchamiania, zwracany format danych) i obsługi błędów. Jeśli czegoś z tego brakuje, wróć do Modułu 5: Kod i sub-workflow.
  • Podstawy OAuth2 z Modułu 4 - do podłączenia credentials Google Sheets. Patrz Moduł 4: Integracje i API, jeśli jeszcze nie zakładałeś takich credentials.
  • Działająca instancja n8n - Cloud, Docker albo dowolny hosting z Modułu 1: Instalacja i hosting.
  • Konto Google z arkuszem i skonfigurowanymi credentials OAuth2 - procedurę zakładania opisuję w sekcji "Credentials Google" niżej, odsyłając do gotowca Faktury z Gmaila na Google Drive, gdzie już to wyjaśniłem.
  • Bota Telegram i jego token - jeśli jeszcze go nie masz, załóż go przez BotFather dokładnie tak, jak w gotowcu Poranny brief na Telegram. Tutaj tej instrukcji nie powtarzam.

Cztery node'y od formularza po powiadomienie - żadnego zewnętrznego narzędzia formularzowego.

Cały workflow w formacie JSON, gotowy do zaimportowania: formularz-arkusz-powiadomienie.json.

  1. Pobierz plik formularz-arkusz-powiadomienie.json (link wyżej).
  2. W n8n otwórz listę workflow i wybierz Import from File (albo przeciągnij plik na kanwę nowego, pustego workflow).
  3. W arkuszu Google Sheets, który chcesz podpiąć, utwórz kartę (tab) o nazwie Zgłoszenia z nagłówkami w pierwszym wierszu dokładnie: Imię, Email, Wiadomość, Data zgłoszenia.
  4. Uzupełnij dwa placeholdery, zanim uruchomisz workflow - patrz tabela niżej.
Placeholder Gdzie Co wpisać
ID_ARKUSZA node Zapisz zgłoszenie (Google Sheets, pole Document, tryb By ID) ID arkusza Google Sheets - fragment adresu URL między /d/ a /edit
TWOJ_CHAT_ID node Wyślij powiadomienie Telegram (pole Chat ID) ID czatu, na który ma trafić powiadomienie

Na koniec podepnij pod node'em Google Sheets swoje credentials OAuth2 i pod node'em Telegram swoje credentials bota - bez tego oba node'y zgłoszą błąd uwierzytelniania przy pierwszym uruchomieniu.

Dodaj node n8n Form Trigger - w przeciwieństwie do zwykłego Webhooka, ten node sam generuje i hostuje stronę formularza pod adresem Twojej instancji n8n, więc nie potrzebujesz Typeform, Google Forms ani JotForm (dokumentacja node'a n8n Form Trigger). W polu Form Title wpisz tytuł widoczny na stronie formularza (np. "Zapisz się"), a w Form Elements dodaj trzy pola:

Pole (Field Name) Element Type Required Field
Imię Text Input tak
Email Email tak
Wiadomość Textarea nie

W sekcji Options dodaj opcję Form Path, żeby ustawić czytelny fragment adresu - w gotowcu to formularz-zgloszenia. Tak jak przy webhookach, formularz ma dwa adresy: Test URL (aktywny po kliknięciu Execute step albo Execute workflow na nieaktywnym workflow - dokładnie ten sam mechanizm, który znasz z Modułu 2: Interfejs i 1. workflow i Modułu 4: Integracje i API) i Production URL (działa dopiero po aktywacji workflow). Zanim wyślesz link do prawdziwych odbiorców, przełącz Active.

W polu Respond When zostaw domyślne "Form Is Submitted", a w Options → Form Response wpisz krótkie podziękowanie (np. "Dziękujemy! Zgłoszenie zostało zapisane.") - to jedyne, co zobaczy osoba wypełniająca formularz.

Dodaj node Code w trybie Run Once for Each Item (tu zawsze jeden item na uruchomienie - jedno zgłoszenie), język JavaScript (dokumentacja node'a Code):

const imie = ($json['Imię'] || '').trim();
const email = ($json['Email'] || '').trim().toLowerCase();
const wiadomosc = ($json['Wiadomość'] || '').trim();
// Guard - formularz ma Required Field, ale nie ufaj wyłącznie walidacji przeglądarki
if (!imie || !email) {
throw new Error('Brak wymaganych danych: pole "Imię" i "Email" są obowiązkowe.');
}
return {
json: {
imie,
email,
wiadomosc,
data_zgloszenia: $json.submittedAt
}
};

Czytając po linijce:

  • $json['Imię'], $json['Email'], $json['Wiadomość'] - to dokładnie te klucze, które Form Trigger nadał polom formularza (patrz notatka w kroku 1). Nawias kwadratowy zamiast kropki dlatego, że klucz z polskim znakiem bywa niewygodny do czytania w notacji $json.pole.
  • .trim() - usuwa białe znaki z brzegów; dla Imię i Email to podwójne zabezpieczenie (Form Trigger i tak już to robi automatycznie), dla Wiadomość to jedyne miejsce, w którym się to dzieje.
  • .toLowerCase() na e-mailu - normalizacja, żeby ten sam adres wpisany raz wielkimi, raz małymi literami nie wyglądał w arkuszu jak dwa różne zgłoszenia.
  • if (!imie || !email) throw new Error(...) - formularz ma wprawdzie Required Field włączone na obu polach, ale nie ufaj wyłącznie walidacji po stronie przeglądarki. throw new Error(...) w Code Node przerywa workflow dokładnie tak samo, jak dedykowany node Stop And Error z Modułu 5, tylko że wewnątrz kodu.
  • data_zgloszenia: $json.submittedAt - Form Trigger sam dokłada do każdego zgłoszenia pole submittedAt (znacznik czasu ISO 8601 momentu wysłania) - nie musisz liczyć go osobnym wyrażeniem $now.
  • Zwrócony pojedynczy obiekt (nie tablica) - to konwencja trybu Run Once for Each Item, który poznałeś w Moduł 5: Kod i sub-workflow: n8n sam opakowuje wynik w listę itemów.

Dodaj node Google Sheets, ustaw Resource: Sheet Within Document i Operation: Append Row - ten sam wzorzec Resource/Operation, co w każdym innym node'cie aplikacji w n8n (patrz Moduł 4: Integracje i API). W polu Document (tryb By ID) wskaż arkusz przez jego ID (placeholder ID_ARKUSZA), a w polu Sheet (tryb By Name) wpisz nazwę karty - w gotowcu to Zgłoszenia. W sekcji mapowania kolumn wybierz Map Each Column Below i przypisz cztery kolumny do pól z poprzedniego kroku:

Kolumna w arkuszu Wartość
Imię {{ $json.imie }}
Email {{ $json.email }}
Wiadomość {{ $json.wiadomosc }}
Data zgłoszenia {{ $json.data_zgloszenia }}

Nagłówki kolumn w arkuszu (pierwszy wiersz karty) muszą się zgadzać z nazwami po lewej stronie dokładnie - wielkość liter i polskie znaki też się liczą. Jeśli je zmienisz, zmień też mapowanie w node'cie. Zweryfikowane pola: dokumentacja node'a Google Sheets.

Credentials OAuth2 dla Google Sheets zakładasz dokładnie tak samo, jak opisuję w sekcji "Credentials Google (Gmail + Drive)" gotowca Faktury z Gmaila na Google Drive - to ten sam mechanizm (Managed OAuth2 na n8n Cloud, własny projekt w Google Cloud Console na self-hostingu), tylko dla innego produktu Google. Pełna procedura: Google OAuth2 single service w dokumentacji n8n oraz Moduł 4: Integracje i API.

Dodaj node Telegram w operacji Send Message, podłącz credentials bota (dokładnie tak, jak zakładasz go w gotowcu Poranny brief na Telegram - stamtąd weźmiesz też swój Chat ID, jeśli jeszcze go nie znasz), wpisz Chat ID (placeholder TWOJ_CHAT_ID) i treść wiadomości wyrażeniem łączącym pola ze zgłoszenia:

Nowe zgłoszenie z formularza!
Imię: {{ $json.imie }}
E-mail: {{ $json.email }}
Wiadomość: {{ $json.wiadomosc || '(brak)' }}

Zweryfikowane pola: dokumentacja node'a Telegram.

  • Objaw: link do formularza, który wysyłasz realnym odbiorcom, zwraca błąd albo w ogóle się nie otwiera, mimo że w edytorze n8n wszystko działało.
  • Przyczyna: tak jak webhook, formularz ma dwa różne adresy - Test URL (aktywny tylko po kliknięciu Execute step/Execute workflow na nieaktywnym workflow) i Production URL (działa stale, ale dopiero gdy workflow jest aktywny) - dokładnie ten sam mechanizm, co przy zwykłym Webhooku, opisany w Moduł 2 i Moduł 4.
  • Naprawa: skopiuj Production URL z node'a Form Trigger (nie Test URL) i włącz przełącznik Active w prawym górnym rogu edytora, zanim wyślesz link dalej.

Krzywe nazwy kolumn w arkuszu - dane trafiają w złe miejsce

Dział zatytułowany „Krzywe nazwy kolumn w arkuszu - dane trafiają w złe miejsce”
  • Objaw: wiersz w arkuszu zapisuje się, ale wartości są przesunięte względem nagłówków albo w ogóle brakuje którejś kolumny.
  • Przyczyna: node Google Sheets w trybie Map Each Column Below dopasowuje dane po dokładnej nazwie nagłówka w pierwszym wierszu karty - jeśli zmienisz nazwę kolumny w arkuszu (albo literówka wkradnie się przy tworzeniu karty) już po skonfigurowaniu node'a, mapowanie przestaje się zgadzać. To udokumentowany, częsty problem - opisuje go wprost sekcja Common Issues node'a Google Sheets.
  • Naprawa: otwórz node Google Sheets i ponownie wybierz tryb mapowania kolumn, żeby n8n pobrał aktualne nagłówki z arkusza na nowo. Najprościej: trzymaj nagłówki w arkuszu w brzmieniu z tabeli w sekcji "Budowa krok po kroku" i nie zmieniaj ich bez poprawienia node'a.

Code Node zatrzymuje się z błędem "Cannot read properties of undefined"

Dział zatytułowany „Code Node zatrzymuje się z błędem "Cannot read properties of undefined"”
  • Objaw: node Waliduj i oczyść dane kończy pracę na czerwono, workflow się zatrzymuje.
  • Przyczyna: najczęściej odwołanie do pola, którego dany item nie ma - np. literówka w nazwie klucza ($json['Imie'] zamiast $json['Imię']) albo zmiana etykiety pola w Form Trigger bez poprawienia kodu. Pełne rozpoznawanie tego błędu opisuję w Coś nie działa? Typowe problemy z n8n.
  • Naprawa: sprawdź w panelu danych node'a Formularz zgłoszeniowy dokładne nazwy kluczy (dokładnie takie, jak Field Name w formularzu) i porównaj z tym, czego szuka kod.

Google Sheets zwraca błąd limitu zapytań przy nagłym ruchu

Dział zatytułowany „Google Sheets zwraca błąd limitu zapytań przy nagłym ruchu”
  • Objaw: node Zapisz zgłoszenie kończy się błędem 429 / "Too many requests" przy większej liczbie zgłoszeń w krótkim czasie.
  • Przyczyna: Google Sheets API domyślnie ogranicza zapisy do 300 żądań na minutę na projekt i 60 żądań na minutę na użytkownika - przy nagłym skoku zgłoszeń (np. link do formularza trafił do dużej grupy naraz) ten limit da się przekroczyć, zgodnie z oficjalną dokumentacją limitów Google Sheets API.
  • Naprawa: w ustawieniach node'a Google Sheets włącz Retry On Fail z odstępem między próbami (patrz Moduł 5: obsługa błędów) - Google zaleca wprost strategię exponential backoff. Realny ruch z jednego formularza rzadko faktycznie przekracza ten limit, więc w praktyce to zabezpieczenie "na wszelki wypadek", a nie codzienna awaria.
  • CRM zamiast arkusza - zamiast Google Sheets podepnij node CRM, którego już używasz (HubSpot, Pipedrive i inne mają dedykowane node'y aplikacji w n8n) - reszta workflow (formularz, Code, Telegram) zostaje bez zmian. To już pełna wersja przepisu "Lead z formularza → CRM → powiadomienie" z Modułu 8: Wzorce i wdrożenia.
  • Autoresponder mailowy - dodaj za node'em Code node wysyłający e-mail powitalny do zgłaszającego (Gmail albo SMTP) - masz już oczyszczony, poprawny adres w polu email.
  • Obsługa błędów jako standard - podepnij dedykowany error workflow z node'em Error Trigger, żeby o nieudanym zapisie do arkusza czy błędzie Telegrama dowiedzieć się od razu, a nie przy najbliższej kontroli arkusza - mechanizm opisuję w Moduł 5: obsługa błędów.
  • Deduplikacja po e-mailu - zanim dopiszesz wiersz, sprawdź node'em Google Sheets (operacja odczytu) albo prostym IF, czy dany e-mail już jest w arkuszu, i pomiń zapis albo zaktualizuj istniejący wiersz zamiast dublować zgłoszenie - to ten sam mechanizm idempotentności, co w Moduł 8.

Co warto zapamiętać

  • n8n Form Trigger sam hostuje formularz pod adresem instancji - bez zewnętrznego narzędzia formularzowego, z tymi samymi zasadami Test URL/Production URL co przy webhookach.
  • Klucz pola w danych to dokładnie Field Name z formularza - trzymaj się jednej pisowni w Form Trigger, Code Node i mapowaniu kolumn arkusza.
  • Walidacja w Code Node (throw new Error(...)) to ta sama zasada co dedykowany node Stop And Error z Modułu 5 - nie ufaj wyłącznie wymaganym polom w interfejsie formularza.
  • Google Sheets w trybie Map Each Column Below dopasowuje dane po dokładnej nazwie nagłówka - literówka albo zmiana nagłówka po skonfigurowaniu node'a psuje mapowanie po cichu.
  • Google Sheets API ma limit zapytań (300/min na projekt, 60/min na użytkownika) - Retry On Fail z odstępem wystarcza na normalny ruch z jednego formularza.

Szukasz kolejnego gotowca do wdrożenia od razu? Zobacz wszystkie gotowe workflow.

Częste pytania

Czy do zbierania zgłoszeń potrzebuję osobnego narzędzia formularzowego, np. Typeform albo Google Forms?

Nie - n8n ma własny n8n Form Trigger, który sam generuje i hostuje stronę formularza pod adresem Twojej instancji, bez rejestracji w kolejnym serwisie. Ten gotowiec korzysta wyłącznie z niego.

Dlaczego mój formularz nie przyjmuje zgłoszeń, mimo że w edytorze n8n działał bez zarzutu?

Najczęściej dlatego, że ktoś dostał Test URL zamiast Production URL, albo workflow nie jest aktywny. Formularz - podobnie jak webhook - ma dwa różne adresy, a produkcyjny działa dopiero po włączeniu przełącznika Active.

Co się stanie, jeśli ktoś spróbuje wysłać formularz bez wypełnienia imienia albo e-maila?

Formularz ma oba pola oznaczone jako Required Field, więc przeglądarka nie pozwoli wysłać pustego formularza. Dodatkowo Code Node ma własny guard ("throw new Error"), który zatrzyma workflow, gdyby mimo to dotarły puste dane - np. przy ręcznym wywołaniu adresu formularza z pominięciem przeglądarki.

Czy mogę zamiast Google Sheets zapisywać zgłoszenia bezpośrednio do CRM?

Tak - to pełna wersja przepisu "Lead z formularza → CRM → powiadomienie" z Modułu 8: Wzorce i wdrożenia. Wystarczy zamienić node Google Sheets na dedykowany node aplikacji Twojego CRM (np. HubSpot czy Pipedrive) - reszta workflow zostaje bez zmian.

Co zrobić, gdy przy nagłym ruchu Google Sheets zwraca błąd limitu zapytań?

Google Sheets API domyślnie ogranicza zapisy do 300 żądań na minutę na projekt i 60 na minutę na użytkownika. Włącz w node'cie Google Sheets Retry On Fail z odstępem między próbami - dla ruchu z jednego formularza to zwykle w zupełności wystarcza.

Czy mogę dodać do formularza więcej pól, niż jest w gotowcu?

Tak - dodaj kolejne pole w Form Elements node'a n8n Form Trigger, obsłuż je w Code Node tak samo jak pozostałe (odwołanie po dokładnej nazwie Field Name) i dodaj odpowiadającą kolumnę w arkuszu oraz w mapowaniu node'a Google Sheets.

made with ❤️ by aitomate.pl - Łukasz Podgórski