JFlow Labs / WIEDZA

Formularz kontaktowy a CRM: integracja, która nie gubi zgłoszeń

Formularz kontaktowy połączysz z CRM-em na trzy sposoby: osadzając na stronie formularz zbudowany w samym CRM-ie, na przykład w HubSpocie albo Pipedrive, wysyłając dane z własnego formularza do API CRM-u albo przekazując je przez pośrednika, takiego jak n8n, Make czy Zapier. Jeśli masz jeden CRM, a zespół pracuje w jego narzędziach, zwykle wystarczy formularz dostawcy; jeśli formularz jest głównym źródłem klientów albo dane mają trafić do kilku systemów, zbuduj własny, który najpierw zapisuje zgłoszenie po Twojej stronie, dopiero potem wysyła je do CRM-u, ponawia wysyłkę przy awarii i nie zakłada drugiego kontaktu dla tego samego adresu e-mail. Zgoda marketingowa to osobne, domyślnie niezaznaczone pole, bo do odpowiedzi na zapytanie zgoda zwykle nie jest potrzebna, a do wysyłania mailem albo telefonicznie ofert, o które ta osoba nie prosiła, art. 398 Prawa komunikacji elektronicznej wymaga uprzedniej zgody.

Trzy drogi z formularza do CRM-u i czym się różnią

Każde połączenie formularza z CRM-em sprowadza się do jednego pytania: gdzie jest zapisane zgłoszenie w chwili, gdy klient widzi komunikat „dziękujemy”. Od odpowiedzi zależy, co się stanie przy pierwszej awarii.

Ten przewodnik nie dotyczy sztucznej inteligencji. Nie ma w nim modeli językowych ani streszczeń, tylko to, co musi działać pod spodem: pola, duplikaty, błędy, spam i zgody. Jeśli planujesz dołożyć do CRM-u kroki z AI, zacznij od tego tekstu, a potem przejdź do przewodnika automatyzacja CRM z AI, bo model dołożony do dziurawego przepływu gubi zgłoszenia tak samo, tylko drożej.

Pierwsza droga to formularz dostawcy: budujesz go w panelu HubSpota albo Pipedrive i wstawiasz na stronę kodem osadzenia. Druga to własny formularz, który wysyła dane do API CRM-u, z przeglądarki albo przez Twój serwer. Trzecia to pośrednik: formularz wysyła dane na webhook w n8n, Make albo Zapierze, a scenariusz zakłada rekordy w CRM-ie. Istnieje jeszcze wariant najstarszy, czyli formularz wysyłający maila, z którego parser wyciąga pola. Działa, dopóki nikt nie zmieni szablonu wiadomości.

Tabela rozbija trzy drogi na sześć wariantów: przechwytywanie formularza przez skrypt HubSpota, wywołanie API z przeglądarki albo z serwera i parser maili. Porównuje je pod względem, który oferty zwykle pomijają: co dzieje się ze zgłoszeniem, gdy CRM albo pośrednik akurat nie odpowiada. To ta kolumna decyduje, czy firma traci klientów, o których nigdy się nie dowie.

Drogi z formularza kontaktowego do CRM-u i ich zachowanie przy awarii.
DrogaGdzie jest zgłoszenie po kliknięciuGdy CRM nie odpowiadaSpam i zgodyKiedy wybrać
Formularz dostawcy osadzony na stronieU dostawcy, od razu w CRM-ie.Zależy od dostawcy; nie masz własnej kopii.Mechanizmy i pola zgód dostawcy.Jeden CRM, zespół pracuje w jego narzędziach.
Przechwytywanie własnego formularza przez skrypt HubSpotaW przeglądarce, w localStorage, dopóki kod śledzący go nie prześle.Jeśli kod śledzący się nie załaduje, zgłoszenie w ogóle nie trafi do HubSpota.Ochrona po Twojej stronie; pola ukryte nie są zbierane.Prosta strona statyczna, gdy akceptujesz straty przy zablokowanych skryptach.
Własny formularz, wywołanie API z przeglądarkiW otwartej karcie, dopóki wywołanie się nie powiedzie.Zgłoszenie ginie, jeśli strona nie ponowi wysyłki.Po Twojej stronie; w HubSpocie zgody idą w obiekcie legalConsentOptions.Mały ruch, brak własnego serwera, wyłącznie publiczny punkt końcowy formularzy.
Własny formularz, Twój serwer, kolejka, APIW Twojej bazie, zanim klient zobaczy podziękowanie.Czeka w kolejce i jest ponawiane; po wyczerpaniu prób przychodzi alert.Pełna kontrola, własny zapis zgód.Formularz jest głównym źródłem klientów albo dane mają trafić do więcej niż jednego systemu.
Pośrednik: n8n, Make, ZapierW kolejce albo w historii wykonań pośrednika.Zależy od ustawień ponowień; kolejka webhooka w Make ma limit.Po stronie formularza; pośrednik tylko przenosi dane.Kilka systemów docelowych i osoba, która utrzyma scenariusze.
Mail z formularza, parser, CRMW skrzynce pocztowej.Mail czeka w skrzynce, ale parser psuje się po zmianie szablonu wiadomości.Po stronie formularza.Rozwiązanie przejściowe, gdy formularza nie da się zmienić.

Formularz dostawcy i przechwytywanie: najmniej pracy, najmniej kontroli

Jeśli masz jeden CRM i nie potrzebujesz niczego poza nim, formularz zbudowany w samym CRM-ie bywa najrozsądniejszym startem. Warto jednak wiedzieć, na co się godzisz.

W HubSpocie formularz tworzysz w panelu i osadzasz na stronie. Zgłoszenie od razu trafia do bazy kontaktów, a deduplikacja działa automatycznie: jeśli kontakt o tym samym adresie e-mail już istnieje, dane z nowego zgłoszenia są dopisywane do istniejącego rekordu. Dokumentacja opisuje też pułapkę: jeśli osoba wyśle formularz, podając swój dodatkowy adres e-mail, ten adres nadpisze dotychczasowy adres kontaktu.

Pipedrive ma Web Forms, czyli formularze udostępniane linkiem albo osadzane na stronie, których zgłoszenia trafiają do Pipedrive jako leady albo transakcje (deals). Funkcja jest częścią dodatku LeadBooster, a konfigurować ją mogą tylko użytkownicy z uprawnieniem administratora transakcji. Ochronę przed spamem zapewnia reCAPTCHA, którą włącza się w ustawieniach przycisku wysyłki. Domyślnego pola zgody Pipedrive nie ma; dokumentacja proponuje obejście w postaci wymaganego pola jednokrotnego wyboru z opcjami Tak i Nie. Wymagane ma być udzielenie odpowiedzi, nie zgoda: formularz z odpowiedzią „Nie” musi dać się wysłać, inaczej powstaje wymuszona zgoda, przed którą ostrzega sekcja o RODO. Zanim wybierzesz tę drogę, sprawdź w cenniku Pipedrive, czy LeadBooster jest już w Twoim planie, czy trzeba go dokupić jako dodatek.

Pośrednim wariantem jest przechwytywanie własnego formularza przez skrypt dostawcy. W HubSpocie to narzędzie non-HubSpot forms: kod śledzący zapisuje wysłane dane w localStorage i przesyła je do HubSpota, który zakłada kontakt albo aktualizuje istniejący o tym samym adresie. Lista wymagań jest długa i trzeba ją przeczytać przed decyzją. Formularz musi być statycznym formularzem HTML w znaczniku form, z polem typu email i zwykłym przyciskiem wysyłki, nie może siedzieć w iframe, nie może mieć JavaScriptu podpiętego pod zdarzenie wysłania, musi istnieć od początku ładowania strony i nie może działać w aplikacji jednostronicowej. Nie może też mieć pól ukrytych, bo narzędzie ich nie zbiera, a to zwykle w nich trzymasz stronę źródłową i parametry kampanii.

Kilka kolejnych zdań z tej samej dokumentacji przesądza sprawę dla firmy, która żyje z formularza. HubSpot zbiera zgłoszenie tylko wtedy, gdy kod śledzący się załaduje, więc konflikt skryptów albo rozszerzenie blokujące skrypty oznacza brak zgłoszenia w HubSpocie. Wszystkie zgłoszenia powiązane z tym samym ciasteczkiem trafiają do jednego kontaktu, nawet z różnymi adresami e-mail, więc dwie osoby piszące z jednego firmowego komputera staną się jednym kontaktem. Każde kliknięcie przycisku wysyłki jest rejestrowane jako zgłoszenie, także przy niewypełnionych wymaganych polach. Sam HubSpot wskazuje tam alternatywę: podłączenie formularza przez API przeznaczone do przesyłania zgłoszeń.

Jest jeszcze zgoda na ciasteczka. Art. 399 Prawa komunikacji elektronicznej pozwala przechowywać informacje w urządzeniu użytkownika po uprzednim poinformowaniu go i uzyskaniu zgody, chyba że jest to konieczne do dostarczenia usługi, której użytkownik żąda. Przechwytywanie HubSpota opiera się na kodzie śledzącym, ciasteczku i localStorage. Czy taki kod mieści się w wyjątku, oceń z prawnikiem; ja przyjmuję, że nie, i ładuję taki kod dopiero po zgodzie w banerze. Konsekwencja: u osoby, która zgody nie dała, przechwytywanie nie zadziała, a jej zapytanie nie trafi do CRM-u.

Własny formularz wysyłający dane do API CRM-u

Własny formularz daje pełną kontrolę nad wyglądem, walidacją i zapisem zgód. W zamian bierzesz na siebie wszystko, co dostawca robił za Ciebie, łącznie z obsługą błędów.

HubSpot ma do tego dwa punkty końcowe w części dokumentacji oznaczonej jako Forms API v3 Legacy. Pierwszy, POST na api.hsforms.com pod ścieżką /submissions/v3/integration/submit/{portalId}/{formGuid}, nie wymaga uwierzytelnienia i obsługuje zapytania CORS, więc zadziała prosto z przeglądarki. Drugi, pod ścieżką /submissions/v3/integration/secure/submit/{portalId}/{formGuid}, wymaga uwierzytelnienia i ma wyższe limity zapytań; ten pasuje do wywołań z serwera. Wysyłasz tablicę pól, do tysiąca pozycji, opcjonalny obiekt context z adresem i nazwą strony, adresem IP i wartością ciasteczka hutk oraz obiekt legalConsentOptions ze zgodami. Zgłoszenie przechodzi przez definicję formularza w HubSpocie, więc działa walidacja formularza i deduplikacja po adresie e-mail.

Publiczny punkt końcowy ma jednak wadę. Identyfikatory konta i formularza są widoczne w kodzie strony, więc bot może wysyłać zgłoszenia prosto do HubSpota, z pominięciem Twojego formularza i wszystkich zabezpieczeń, które w nim zbudowałeś. Przy formularzu, który dostaje dużo śmieci, to argument za wywołaniem z serwera.

Dokumentacja wylicza typy błędów i warto obsłużyć je osobno, bo mówią, czy ponawiać. BLOCKED_EMAIL, INVALID_EMAIL, REQUIRED_FIELD czy FIELD_NOT_IN_FORM_DEFINITION to błędy danych albo konfiguracji: ponowienie niczego nie zmieni. MISSING_PROCESSING_CONSENT sygnalizuje brak zgody, której wymaga formularz, a FORM_HAS_RECAPTCHA_ENABLED oznacza reCAPTCHA włączoną w definicji formularza. Odpowiedź 429 to przekroczony limit i ją ponawiasz z odczekaniem. Pole submittedAt pozwala przekazać prawdziwy moment wysłania przy dostarczaniu z kolejki, ale znacznik starszy niż miesiąc kończy się błędem.

Alternatywą jest pominięcie formularzy i zapis prosto do obiektów CRM przez API kontaktów. Wtedy nie powstaje zgłoszenie formularza, tylko zwykły zapis rekordu, więc deduplikację robisz sam. HubSpot zaleca zawsze podawać e-mail, bo to podstawowy unikalny identyfikator kontaktu, a do zapisu bez duplikatów służy upsert: POST na /crm/objects/2026-03/contacts/batch/upsert z parametrem idProperty ustawionym na email albo na własną właściwość z unikalnymi wartościami. Istniejący kontakt zostanie zaktualizowany, nieistniejący utworzony. Przy polach z listą opcji podajesz nazwy wewnętrzne, a nie etykiety, bo nazwa wewnętrzna zostaje taka sama nawet po zmianie etykiety.

W Pipedrive zapisujesz dane przez API osób, organizacji i leadów, zawsze z serwera. Lead musi być powiązany z osobą albo organizacją, a dokumentacja dodawania leada mówi wprost, że jeśli osoba jeszcze nie istnieje, trzeba ją najpierw utworzyć. Sekwencja wygląda więc tak: wyszukanie osoby po adresie e-mail (GET /api/v2/persons/search), jej utworzenie, jeśli nie istnieje (POST /api/v2/persons), i dopiero potem lead (POST /api/v1/leads) z wypełnionym person_id. Lead utworzony przez API dostaje źródło i pochodzenie ustawione na API; kanał marketingowy wskazujesz polem channel, ale wartość musi należeć do kanałów skonfigurowanych w Twojej firmie. Uważaj na właściciela: jeśli pominiesz owner_id, właścicielem leada zostanie użytkownik, w którego imieniu działa integracja.

Jedna zasada nie ma wyjątków: klucz API ani token prywatnej aplikacji nie trafia do kodu w przeglądarce. Token w JavaScripcie strony jest publiczny, a daje dostęp do całego CRM-u. Z przeglądarki wołasz tylko to, co zaprojektowano jako publiczne; resztę robi Twój serwer.

Pośrednik: n8n, Make, Zapier i co znaczy odpowiedź 200

Pośrednik ma sens, gdy jedno zgłoszenie ma trafić do kilku miejsc naraz. Trzeba tylko rozumieć, że szybka odpowiedź webhooka nie oznacza, że rekord jest już w CRM-ie.

Który z trzech wybrać, rozkłada na czynniki pierwsze przewodnik n8n, Make czy Zapier. Tu liczy się jedno: jak każdy z nich odpowiada formularzowi i co robi ze zgłoszeniem, którego nie da się od razu zapisać.

Make bez modułu Webhook response odpowiada domyślnie kodem 200 z treścią Accepted, co znaczy tylko tyle, że dane trafiły do kolejki webhooka. Kolejka ma limit zależny od subskrypcji, z górną granicą 10 000 pozycji, a gdy jest pełna, Make odrzuca nadmiarowe dane odpowiedzią 400 z komunikatem Queue is full. Przy więcej niż 300 zapytaniach w ciągu 10 sekund zwraca 429. Webhook, który przez ponad pięć dni nie jest podłączony do żadnego scenariusza, zostaje automatycznie wyłączony i odpowiada kodem 410 Gone. Każdą z tych odpowiedzi formularz musi potraktować jako porażkę, a nie jako powód do podziękowania.

W n8n węzeł Webhook ma kilka trybów odpowiedzi. Tryb Immediately zwraca kod i komunikat Workflow got started, czyli potwierdza start przepływu, a nie zapis. Tryb When Last Node Finishes czeka na koniec przepływu i oddaje wynik ostatniego węzła. Trzecia możliwość to osobny węzeł Respond to Webhook, w którym sam decydujesz, co i kiedy odpowiedzieć. Jeśli formularz ma wiedzieć, czy zapis w CRM-ie się udał, odpowiedź musi paść po węźle zapisu, a nie na starcie. Opcja Allowed Origins, czyli lista domen dopuszczonych przez CORS, domyślnie przepuszcza wszystkie; zawęź ją do swojej domeny.

Klasyczny błąd przy n8n to formularz wpięty w zły adres. Węzeł Webhook ma adres testowy, rejestrowany, gdy w edytorze nasłuchujesz zdarzenia testowego, i produkcyjny, rejestrowany dopiero po opublikowaniu przepływu. Formularz podpięty pod adres testowy działa tylko wtedy, gdy ktoś akurat nasłuchuje w edytorze: na prezentacji wszystko gra, w nocy zgłoszenia przepadają.

Jeśli używasz polskiego Livespace, jego baza wiedzy opisuje połączenie formularza przez Zapiera: formularz wysyła maila na skrzynkę Email Parser, parser wyciąga pola, a Zapier zakłada w Livespace osobę i szansę sprzedaży. Instrukcja przypomina też, że klucze API Livespace pozwalają działać w imieniu użytkownika bez znajomości hasła, więc chronisz je jak dane logowania. Zaleta tej drogi: mail czeka w skrzynce, gdy coś po drodze nie działa. Wada: psuje się po cichu, gdy ktoś zmieni treść maila z formularza.

Moja zasada przy pośrednikach jest taka: formularz nie wysyła danych prosto do webhooka pośrednika, tylko najpierw zapisuje je u siebie, a pośrednik jest jednym z odbiorców. Wtedy wyłączony scenariusz, pełna kolejka czy zmieniony adres webhooka kosztują opóźnienie, a nie zgłoszenie.

Mapowanie pól: co z formularza trafia do którego obiektu

Mapowanie to lista decyzji, którą warto spisać przed pierwszą linijką kodu: które pole formularza trafia do którego pola w CRM-ie, w jakim formacie i co się dzieje, gdy wartość już tam jest.

Zacznij od obiektów, nie od pól. W HubSpocie zgłoszenie zakłada albo aktualizuje kontakt, a firma jest osobnym obiektem, który HubSpot dopasowuje po domenie tylko wtedy, gdy zgłoszenie formularza zawiera domenę firmy. Przy zapisie przez API obiektów HubSpot nie deduplikuje firm po domenie; dopasowanie robisz sam. W Pipedrive masz trzy poziomy: osobę, organizację i lead, który musi wskazywać osobę albo organizację. Leady w Pipedrive nie mają własnego zestawu pól niestandardowych, tylko dziedziczą strukturę pól z transakcji, więc pole „czego dotyczy zapytanie” zakładasz w polach transakcji.

Imię i nazwisko to częsta pułapka. Pipedrive przy tworzeniu osoby wymaga jednego pola name, HubSpot rozdziela firstname i lastname. Nie dziel automatycznie wpisu „Anna Maria Kowalska-Nowak” po pierwszej spacji, bo zepsujesz rekord, który potem ktoś będzie poprawiał ręcznie. Jeśli CRM rozdziela pola, rozdziel je w formularzu. W HubSpocie do utworzenia kontaktu wystarczy jedno z trzech pól: email, firstname albo lastname, ale dokumentacja zaleca zawsze podawać e-mail.

Normalizuj dane przed zapisem, nie po. Z adresu e-mail usuń spacje na początku i na końcu, a adresy porównuj bez rozróżniania wielkości liter; wyszukiwanie osób w Pipedrive z parametrem exact_match też nie rozróżnia wielkości liter. Telefon zapisuj w formacie międzynarodowym z prefiksem kraju, jak w przykładzie z dokumentacji HubSpota. Wartości list bierz z zamkniętego słownika zgodnego z nazwami wewnętrznymi opcji w CRM-ie.

Pola techniczne, których użytkownik nie widzi, są równie ważne: strona źródłowa, parametry kampanii, identyfikator zgłoszenia i wersja tekstu zgód. W Forms API HubSpota adres i tytuł strony idą w obiekcie context jako pageUri i pageName; w Pipedrive zapisujesz je w polach niestandardowych.

Przykładowe mapowanie pól formularza kontaktowego na HubSpot i Pipedrive.
Pole formularzaHubSpotPipedriveNa co uważać
E-mailWłaściwość email kontaktu; klucz deduplikacji.Tablica emails osoby, adres oznaczony jako primary.Usunięcie spacji z początku i końca oraz porównanie bez rozróżniania wielkości liter przed wyszukaniem.
Imię i nazwiskofirstname i lastname.name osoby, pole wymagane.Nie dziel jednego pola automatycznie po spacji.
Telefonphone.Tablica phones osoby.Format międzynarodowy z prefiksem kraju.
Firmacompany albo powiązany obiekt firmy.Organizacja utworzona przed leadem; potem organization_id leada i org_id osoby.HubSpot dopasowuje firmę po domenie tylko przy zgłoszeniu formularza z domeną firmy; przy zapisie przez API obiektów robisz to sam.
Treść wiadomościPole wielowierszowe albo notatka powiązana z kontaktem.Notatka albo pole niestandardowe leada.Limit długości w formularzu, zapis jako zwykły tekst.
Temat zapytaniaWłaściwość z listą opcji, podawana nazwą wewnętrzną.Pole niestandardowe dziedziczone z transakcji.Jeden słownik wartości dla formularza i CRM-u.
Strona źródłowa i kampaniacontext.pageUri i context.pageName w Forms API.Pola niestandardowe; źródło leada ustawia się na API.Przechwytywanie HubSpota nie zbiera pól ukrytych.
Zgoda marketingowalegalConsentOptions.consent.communications z treścią zgody.marketing_status przy produkcie Campaigns, w innym wypadku pole niestandardowe.Osobno od zgody na kontakt, z datą i brzmieniem.
Identyfikator zgłoszeniaWłasna właściwość z unikalnymi wartościami, którą można podać jako idProperty (tylko przy zapisie przez API obiektów, nie przez Forms API).Pole niestandardowe.Ten sam identyfikator przy każdym ponowieniu.

Duplikaty: jeden człowiek, jeden kontakt

Duplikaty nie biorą się z pecha, tylko z trzech konkretnych sytuacji: ponownego kliknięcia, ponowienia po częściowym sukcesie i dwóch zgłoszeń tej samej osoby w krótkim odstępie. Każda ma inne lekarstwo.

Pierwsza sytuacja to podwójne wysłanie: klient kliknął dwa razy albo przeglądarka powtórzyła żądanie po zerwanym połączeniu. Rozwiązaniem jest identyfikator zgłoszenia nadawany w przeglądarce raz dla danej treści formularza i zapisywany razem ze zgłoszeniem. Serwer, który drugi raz dostaje ten sam identyfikator, zwraca wynik pierwszego zapisu, zamiast tworzyć nowy. Tak działa formularz kontaktowy na tej stronie: identyfikator powstaje w przeglądarce, a ponowne wysłanie tej samej treści nie zakłada drugiego zgłoszenia.

Druga sytuacja to ponowienie po częściowym sukcesie. W Pipedrive zapis to co najmniej dwa kroki, osoba i lead. Jeśli osoba się utworzyła, a zapis leada skończył się przekroczeniem czasu, naiwne ponowienie całości założy drugą osobę. Dlatego po każdym udanym kroku zapisuję przy zgłoszeniu identyfikator zwrócony przez CRM, a ponowienie zaczyna od pierwszego kroku, którego wynik nie jest jeszcze zapisany.

Trzecia sytuacja to wyścig: dwa zgłoszenia tej samej osoby w odstępie sekund, przetwarzane równolegle. Oba wyszukiwania nikogo nie znajdują i oba zakładają osobę. W HubSpocie adres e-mail jest podstawowym identyfikatorem unikalnym, a upsert po nim aktualizuje istniejący kontakt, zamiast tworzyć nowy. W Pipedrive wyszukanie i utworzenie to dwa osobne wywołania, więc zgłoszenia z tym samym adresem trzeba przetwarzać po kolei. Make przy webhookach natychmiastowych domyślnie uruchamia wykonania równolegle, a przetwarzanie po kolei włączasz w ustawieniach scenariusza opcją process data in order, przy której następne wykonanie czeka na zakończenie poprzedniego. We własnej kolejce wystarczy blokada na znormalizowany adres.

W Pipedrive samo sprawdzenie zużywa dzienny budżet tokenów API: w API v2 wyszukanie osoby to 20 jednostek, jej dodanie 5, a wyszukiwarka ma osobny limit 10 zapytań na 2 sekundy na każdym planie. Przy kilkunastu zgłoszeniach dziennie to bez znaczenia, przy przenoszeniu tysięcy starych zgłoszeń trzeba to zaplanować.

Istniejące duplikaty scala się narzędziami CRM-u: HubSpot ma punkt końcowy scalania kontaktów, Pipedrive scalania osób, a w HubSpocie rekord, który zostaje, łączy aktywności, powiązania i większość właściwości obu. Nie podpinaj scalania pod automat wyzwalany formularzem; to decyzja dla człowieka.

Gdy API CRM-u nie odpowiada: kolejka, ponowienia i alert

Zasada jest jedna: zgłoszenie ma być trwale zapisane po Twojej stronie, zanim klient zobaczy podziękowanie. Wszystko inne da się naprawić później, zgubionego zgłoszenia nie.

Buduję to tak: formularz wysyła dane do serwera strony, serwer je sprawdza, zapisuje w bazie ze statusem „do dostarczenia” i dopiero wtedy odpowiada przeglądarce sukcesem. Dostarczeniem do CRM-u zajmuje się osobny proces, który pobiera zgłoszenia z kolejki, wysyła je i oznacza jako dostarczone. Jeśli CRM nie odpowiada, zgłoszenie czeka. Klient dostał potwierdzenie, handlowiec powiadomienie, a CRM dostanie rekord z opóźnieniem.

Formularz kontaktowy na tej stronie działa według tej samej zasady, choć nie stoi za nim CRM: zgłoszenie jest najpierw zapisywane w bazie, dopiero potem wychodzą maile, a jeśli wysyłka się nie uda, osoba widzi komunikat, że zapytanie jest zapisane, razem z numerem zgłoszenia, zamiast prośby o ponowne wpisanie wszystkiego.

Kluczowe jest rozróżnienie błędów. Przejściowe ponawiasz: 429, 502, 503, 504 i przekroczenie czasu. HubSpot w dokumentacji obsługi błędów przy 503 oraz przy przekroczeniach czasu 502 i 504 zaleca wstrzymać zapytania na kilka sekund i ponowić. Trwałe kierujesz od razu do ręcznej kolejki: błąd walidacji 400, zablokowany adres, pole spoza definicji formularza. Ponawianie ich w pętli niczego nie naprawi, a zużywa limit. Osobno traktuj 401 i 403: wygasły albo cofnięty token nie naprawi się sam, więc taki błąd od razu wywołuje alert, a zgłoszenia czekają w kolejce i są ponawiane dopiero po poprawieniu danych dostępowych.

Ponowienia rozkładaj w czasie, coraz rzadziej, i czytaj nagłówki limitów. Pipedrive zwraca x-ratelimit-limit, x-ratelimit-remaining i x-ratelimit-reset dla dwusekundowego okna, a jego dzienny budżet tokenów jest wspólny dla wszystkich użytkowników firmy i odnawia się o północy w strefie czasowej serwera, która nie musi być taka sama jak Twoja. Po wyczerpaniu budżetu wszystkie zapytania API dostają 429 aż do odnowienia, więc staje każda integracja firmy, a integracje na tokenie API, które mimo 429 dalej wysyłają dużo zapytań, Pipedrive blokuje odpowiedzią 403.

Liczba prób nie może być nieskończona. Po jej wyczerpaniu zgłoszenie przechodzi w stan „wymaga uwagi”, a konkretna osoba dostaje alert z identyfikatorem zgłoszenia i treścią błędu. Raz dziennie porównaj, ile zgłoszeń przyjął formularz i ile rekordów z tego dnia ma CRM; rozjazd widzisz, zanim klient zapyta, dlaczego nikt nie odpisał.

Spam bez uciążliwej CAPTCHA

Każdy publiczny formularz dostaje śmieci. Celem nie jest zero spamu na wejściu, tylko zero spamu w CRM-ie i żadnego zablokowanego prawdziwego klienta.

CAPTCHA jest najprostsza do włączenia i najdroższa dla ludzi. W3C w dokumencie o niedostępności CAPTCHA pisze, że sama natura interaktywnego testu wyklucza wiele osób z niepełnosprawnościami. Do tego dochodzi konflikt techniczny: dla formularza HubSpota z reCAPTCHA włączoną w definicji dokumentacja Forms API przewiduje błąd FORM_HAS_RECAPTCHA_ENABLED, więc przy własnym formularzu ochronę i tak budujesz u siebie. Zanim sięgniesz po test dla człowieka, ustaw warstwy, których prawdziwy klient w ogóle nie zauważy.

Formularz na tej stronie ma pole-pułapkę, limit zgłoszeń na adres e-mail w oknie godzinowym i dzienny limit dla całego formularza, bez CAPTCHA. To zestaw, od którego zaczynam, a dopiero gdy nie wystarcza, dokładam kolejną warstwę.

Zgody i RODO: co formularz ma powiedzieć i co zapisać

Częsty błąd w polskich formularzach to jeden wymagany checkbox „wyrażam zgodę na przetwarzanie danych”, który ma załatwić wszystko naraz. Żadnej z tych rzeczy nie załatwia dobrze.

Żeby odpowiedzieć na zapytanie, zwykle nie potrzebujesz zgody. Art. 6 ust. 1 lit. b RODO pozwala przetwarzać dane, gdy jest to niezbędne do podjęcia działań na żądanie osoby przed zawarciem umowy, a prośba o ofertę jest takim żądaniem. Europejska Rada Ochrony Danych w wytycznych 05/2020 wskazuje, że gdy przetwarzanie jest rzeczywiście niezbędne do wykonania umowy, zgoda nie jest właściwą podstawą, a jeśli zgoda jest wpleciona w niepodlegające negocjacji warunki, domniemywa się, że nie została wyrażona dobrowolnie. Podstawę dla swojej sytuacji ustal z prawnikiem; formularz nie powinien prosić o zgodę, której nie potrzebuje.

Obowiązek informacyjny obowiązuje niezależnie od podstawy. Art. 13 RODO wymaga podania przy zbieraniu danych między innymi tożsamości administratora, celów i podstawy prawnej, odbiorców danych, okresu przechowywania albo kryteriów jego ustalenia, praw osoby i prawa skargi do organu nadzorczego, a gdy dane mogą trafić do państwa trzeciego, także tej informacji. Przy integracji z CRM-em dwie pozycje wymagają wiedzy technicznej: odbiorcy, czyli dostawca CRM-u i pośrednik, oraz okres przechowywania, którego CRM sam nie pilnuje. Oba podmioty przetwarzają dane w Twoim imieniu, więc art. 28 RODO wymaga, żebyś korzystał wyłącznie z podmiotów przetwarzających, które zapewniają wystarczające gwarancje, i miał z nimi umowę powierzenia.

Zgoda marketingowa to osobne pole, z trzech powodów. Art. 7 ust. 2 RODO wymaga, by prośba o zgodę była wyraźnie odróżnialna od pozostałych kwestii, a motyw 32 mówi, że okienka domyślnie zaznaczone nie powinny oznaczać zgody. Wytyczne EROD wymagają osobnej zgody na każdy cel: newsletter, telefon handlowy i przekazanie danych partnerom zlepione w jedno pole sprawiają, że zgoda nie jest dobrowolna. Trzeci powód w Polsce decyduje o wszystkim: art. 398 Prawa komunikacji elektronicznej zakazuje używania telekomunikacyjnych urządzeń końcowych, w szczególności w ramach usług komunikacji interpersonalnej, oraz automatycznych systemów wywołujących do przesyłania informacji handlowej, w tym marketingu bezpośredniego, bez uprzedniej zgody odbiorcy. W praktyce dotyczy to maili z ofertami, o które odbiorca nie prosił, i telefonów handlowych, a art. 400 każe przy tej zgodzie stosować odpowiednio przepisy o ochronie danych osobowych.

Motyw 47 RODO dopuszcza uznanie marketingu bezpośredniego za prawnie uzasadniony interes administratora, ale rozstrzyga to wyłącznie kwestię podstawy z RODO. Wysłanie oferty mailem podlega osobnym przepisom, a zgodę z art. 398 musisz mieć niezależnie od tego. Jeśli Twoja klauzula powołuje się jeszcze na art. 10 ustawy o świadczeniu usług drogą elektroniczną albo na Prawo telekomunikacyjne, jest nieaktualna: ustawa wprowadzająca Prawo komunikacji elektronicznej uchyliła ten artykuł, a Prawo telekomunikacyjne utraciło moc. Nowe przepisy obowiązują od 10 listopada 2024 r.

Art. 7 ust. 1 RODO wymaga, żebyś umiał wykazać, że zgoda została wyrażona. EROD podpowiada, co to znaczy w praktyce: zapis, który pozwala pokazać, jak i kiedy zgodę uzyskano i jakie informacje osoba wtedy dostała, przy czym dowód zgody nie powinien prowadzić do zbierania większej ilości danych, niż to konieczne. Z tego wynika minimalny zestaw zapisywany przy każdym zgłoszeniu.

Mail z potwierdzeniem i powiadomienie dla handlowca

Potwierdzenie ma powiedzieć klientowi, że zgłoszenie dotarło, i niewiele więcej. Każde dodatkowe zdanie to ryzyko: prawne, wizerunkowe albo techniczne.

Wysyłaj potwierdzenie po trwałym zapisie zgłoszenia, a nie po udanym zapisie w CRM-ie. Klient nie powinien czekać na maila tylko dlatego, że API dostawcy akurat odpowiada 503. Z tego samego powodu powiadomienie dla handlowca nie może zależeć wyłącznie od reguł w CRM-ie: jeśli rekord utknął w kolejce, handlowiec i tak powinien wiedzieć, że ktoś czeka na odpowiedź.

W treści wystarczy numer zgłoszenia, krótkie podsumowanie tematu, informacja, kiedy realnie odpiszesz, i adres, na który można napisać, żeby poprawić dane. Jeśli dołączasz kopię wiadomości, wstawiaj ją jako zwykły tekst z zamienionymi znakami HTML i nigdy nie buduj z treści zgłoszenia klikalnych linków. Formularz przyjmuje dowolny adres e-mail, więc ktoś może wpisać cudzy adres i treść z linkiem, a Twój serwer roześle to z Twojej domeny. Limit zgłoszeń na adres e-mail z sekcji o spamie ogranicza taką wysyłkę.

Nie dokładaj do potwierdzenia oferty, kodu rabatowego ani zaproszenia na webinar, jeśli nadawca nie zaznaczył zgody marketingowej. Takie dodatki mogą zrobić z potwierdzenia informację handlową w rozumieniu art. 398 Prawa komunikacji elektronicznej, a na nią potrzebujesz uprzedniej zgody.

Potwierdzenie wychodzi z Twojej domeny, więc domena musi mieć poprawnie ustawione rekordy SPF, DKIM i DMARC; bez nich rośnie ryzyko, że trafi do spamu, a klient uzna, że formularz nie działa. Adres nadawcy powinien przyjmować odpowiedzi, bo ludzie odpisują na potwierdzenie z uzupełnieniem. Jeśli samo potwierdzenie się nie wyśle, nie cofaj przyjęcia zgłoszenia: pokaż numer na ekranie i ponów wysyłkę z kolejki.

Jak przetestować całość od kliknięcia do rekordu

Integrację formularza testuje się scenariuszami awarii, nie tylko scenariuszem, w którym wszystko działa. Ten zadziała pierwszego dnia, awarie przyjdą w trzecim miesiącu.

Testuj na osobnym koncie, a nie na produkcyjnej bazie klientów. Sprawdź, czy Twój CRM daje konto testowe albo sandbox dla deweloperów, i podłącz do niego kopię formularza na stronie testowej. Jeśli takiej możliwości nie masz, oznaczaj rekordy testowe jednoznacznie, na przykład stałym dopiskiem w nazwisku i adresem z osobnej domeny, i usuwaj je po testach, żeby nie zafałszowały raportów sprzedaży.

Przy n8n sprawdź, że formularz wskazuje produkcyjny adres webhooka. Przy Make sprawdź, co dostaje formularz przy wyłączonym scenariuszu.

  1. Wszystko działaWyślij formularz z telefonu i z komputera. Sprawdź w CRM-ie każde pole z tabeli mapowania, właściciela, źródło i zgody, a w skrzynce potwierdzenie.
  2. Ten sam adres dwa razyDrugie zgłoszenie z tym samym adresem, wpisanym wielkimi literami i ze spacją na końcu. Oczekiwany wynik: jeden kontakt i dwie wiadomości w jego historii.
  3. Podwójne kliknięcie i powtórzone żądanieKliknij „wyślij” dwa razy i powtórz żądanie z tym samym identyfikatorem zgłoszenia. Oczekiwany wynik: jedno zgłoszenie i jeden rekord.
  4. CRM nie działaPodmień token na nieprawidłowy albo zablokuj ruch wychodzący do API. Klient ma zobaczyć sukces, zgłoszenie ma czekać w kolejce, alert ma przyjść. Po przywróceniu dostępu zgłoszenie ma trafić do CRM-u dokładnie raz.
  5. Przekroczony limitZasymuluj odpowiedź 429. Sprawdź w logach, że kolejne próby są rozłożone w czasie, a nie wysyłane w pętli.
  6. Błąd trwałyWyślij wartość spoza słownika listy. Zgłoszenie ma od razu trafić do ręcznej kolejki, bez ponowień.
  7. SpamWypełnij pole-pułapkę i wyślij serię zgłoszeń z jednego adresu. Nic z tego nie może trafić do CRM-u, a prawdziwe zgłoszenie wysłane zaraz potem z innego adresu musi przejść.
  8. ZgodyWyślij formularz bez zgody marketingowej i ze zgodą. W CRM-ie mają być dwie różne wartości z datą i brzmieniem zgody, a wycofanie zgody ma zmienić status w CRM-ie.
  9. Uzgodnienie po tygodniuPorównaj liczbę zgłoszeń przyjętych przez formularz z liczbą rekordów w CRM-ie z tego samego okresu. Każda różnica ma mieć wyjaśnienie.

Kiedy to przesada i co wybrać w zamian

Kolejka, identyfikatory zgłoszeń i ręczna lista błędów to praca, która ma sens tylko wtedy, gdy formularz realnie przynosi klientów.

Jeśli dostajesz kilka zapytań w miesiącu i obsługuje je jedna osoba, która i tak odpisuje z maila, integracja z CRM-em może poczekać. Wystarczy formularz, który niezawodnie wysyła maila i zapisuje kopię zgłoszenia, oraz nawyk ręcznego wpisu do CRM-u. Automatyzacja kilku rekordów miesięcznie kosztuje więcej, niż oszczędza.

Jeśli pracujesz w HubSpocie, a marketing buduje na formularzach raporty i kampanie, osadzony formularz HubSpota jest zwykle lepszy niż własny: tracisz część kontroli nad wyglądem, zyskujesz narzędzia, które zespół utrzyma bez programisty. Podobnie w Pipedrive, gdy firma ma już LeadBooster z Web Forms.

Własny formularz z kolejką ma sens, gdy formularz jest głównym źródłem klientów, gdy dane mają trafić do więcej niż jednego systemu, gdy strona jest aplikacją jednostronicową, której przechwytywanie HubSpota nie obsłuży, albo gdy potrzebujesz pełnej kontroli nad zapisem zgód. Taką integrację buduję w ramach integracji API, a widełki są w cenniku. Wariant z pośrednikiem, w którym zapytanie z formularza przechodzi przez n8n do CRM-u, jest w cenniku opisany przy automatyzacjach AI.

Nie budowałbym też własnego rozwiązania, jeśli po Twojej stronie nikt nie będzie czytał alertów. Kolejka bez człowieka, który raz w tygodniu przejrzy zgłoszenia wymagające uwagi, przesuwa problem w czasie, ale go nie rozwiązuje.

Pytania i odpowiedzi

Jak połączyć formularz kontaktowy z CRM-em bez programisty?

Najprościej formularzem zbudowanym w samym CRM-ie i osadzonym na stronie. W HubSpocie zgłoszenie od razu zakłada albo aktualizuje kontakt o tym samym adresie e-mail. W Pipedrive służą do tego Web Forms z dodatku LeadBooster, więc sprawdź, czy masz go w planie, czy trzeba go dokupić. Druga droga bez kodu to pośrednik, taki jak n8n, Make albo Zapier; pamiętaj wtedy, że odpowiedź webhooka Accepted oznacza przyjęcie do kolejki, a nie zapis w CRM-ie.

Czy mogę wysyłać dane z formularza prosto z przeglądarki do API CRM-u?

Tylko do punktu końcowego zaprojektowanego jako publiczny, jak niewymagający uwierzytelnienia punkt końcowy formularzy HubSpota, który obsługuje zapytania CORS. Tokenu prywatnej aplikacji ani klucza API nie umieszczaj w kodzie strony, bo każdy odwiedzający może go odczytać. Przy wywołaniu z przeglądarki zgłoszenie istnieje tylko w otwartej karcie: jeśli wywołanie się nie uda, a strona go nie ponowi, przepada.

Co się dzieje ze zgłoszeniem, gdy CRM ma awarię?

To zależy od tego, gdzie zgłoszenie jest zapisane w chwili wysłania. Jeśli formularz woła API bezpośrednio, zgłoszenie ginie razem z nieudanym wywołaniem. Jeśli najpierw zapisujesz je u siebie, czeka w kolejce i jest ponawiane. HubSpot przy odpowiedziach 502, 503 i 504 zaleca odczekać kilka sekund i ponowić, a Pipedrive po wyczerpaniu dziennego budżetu tokenów odpowiada 429 aż do jego odnowienia. Pełna kolejka webhooka w Make odrzuca nadmiarowe dane odpowiedzią 400.

Jak uniknąć duplikatów kontaktów z formularza?

Kluczem dopasowania jest znormalizowany adres e-mail: bez spacji na początku i na końcu, porównywany bez rozróżniania wielkości liter. W HubSpocie zapisuj przez upsert z idProperty ustawionym na email, a przez Forms API deduplikacja po adresie dzieje się sama. W Pipedrive najpierw wyszukaj osobę z parametrem exact_match, utwórz ją tylko wtedy, gdy nie istnieje, i dopiero potem dodaj lead. Do tego identyfikator zgłoszenia nadawany w przeglądarce, żeby podwójne kliknięcie nie tworzyło dwóch rekordów, oraz przetwarzanie po kolei zgłoszeń z tym samym adresem.

Czy checkbox ze zgodą na przetwarzanie danych w formularzu kontaktowym jest obowiązkowy?

Do samej odpowiedzi na zapytanie zwykle nie, bo podstawą może być art. 6 ust. 1 lit. b RODO, czyli działania na żądanie osoby przed zawarciem umowy; konkretną podstawę ustal z prawnikiem. Obowiązkowa jest natomiast informacja z art. 13 RODO. Zgoda na marketing to osobne, domyślnie niezaznaczone pole, a do wysyłania mailem lub telefonicznie ofert, o które ta osoba nie prosiła, art. 398 Prawa komunikacji elektronicznej wymaga uprzedniej zgody. Zapisuj jej brzmienie, datę, adres strony i każdą wartość osobno.

Czy formularz kontaktowy potrzebuje CAPTCHA?

Zwykle nie na start. Pole-pułapka odrzucane na serwerze, limit zgłoszeń na adres e-mail, walidacja długości i kwarantanna podejrzanych zgłoszeń odsiewają proste boty bez utrudniania życia ludziom. Sprawdzenie nagłówka Origin zatrzyma tylko wysyłki z przeglądarek na cudzych stronach, bo bot działający poza przeglądarką poda ten nagłówek sam. W3C zwraca uwagę, że interaktywne testy CAPTCHA wykluczają wiele osób z niepełnosprawnościami. Przy własnym formularzu wysyłanym do HubSpota przez Forms API dokumentacja przewiduje błąd FORM_HAS_RECAPTCHA_ENABLED, gdy w definicji formularza włączona jest reCAPTCHA, więc ochronę budujesz po swojej stronie.

Jak sprawdzić, czy integracja nie gubi zgłoszeń?

Porównuj codziennie dwie liczby: ile zgłoszeń przyjął formularz i ile rekordów z tego samego dnia ma CRM. Do tego alert do konkretnej osoby dla każdego zgłoszenia, które po wyczerpaniu prób przeszło do ręcznej kolejki. Przed startem przejdź scenariusze awarii: podwójne kliknięcie, ten sam adres dwa razy, wyłączony CRM, odpowiedź 429, błąd walidacji i spam. Testuj na osobnym koncie albo na jednoznacznie oznaczonych rekordach testowych, nigdy na żywej bazie bez oznaczeń.

Źródła

Liczby i terminy w tym przewodniku sprawdziłem w oficjalnych źródłach. Ceny i przepisy się zmieniają, więc przy decyzji warto zajrzeć do nich ponownie.

Masz pytanie do swojego projektu?

Opisz, co chcesz osiągnąć. Odpowiem konkretnie, także wtedy, gdy najlepszą odpowiedzią jest „nie rób tego”.

Jonasz Jankowski · Bezpośrednia współpraca · Mikołów (Śląsk), zdalnie w całej Polsce

CO DALEJ PO WYSŁANIU
  1. Odpowiadam osobiście w ciągu jednego dnia roboczego.
  2. Krótka rozmowa o celu, funkcjach, materiałach i integracjach.
  3. Dostajesz zakres z etapami i konkretną kwotę, zanim cokolwiek zaczniemy.

Pola oznaczone jako opcjonalne możesz pominąć. Odpowiadam na każdą wiadomość.

Minimum 20 znaków. Wystarczy zalążek pomysłu.0/5000

Na podany e-mail otrzymasz potwierdzenie i kopię swojej wiadomości. Odpowiadam w ciągu jednego dnia roboczego. Dalszą rozmowę możemy prowadzić w tym samym wątku.