TL;DR
Formularz kontaktowy w Next.js nie musi bezpośrednio wołać pięciu API (CRM, Slack, email, Google Sheets, newsletter). n8n jako orchestrator przyjmuje jeden webhook z Server Action, waliduje payload, zapisuje lead do CRM, wysyła wiadomość na Slack i loguje błędy — z retry i wizualnym debugowaniem flow. Next.js robi to, co robi najlepiej: walidacja Zod, rate limit, zapis kopii w PostgreSQL (Branchly), potem fire-and-forget POST do n8n. Hosting aplikacji na DevStudioIT Cloud, n8n na VPS lub n8n.cloud — webhook URL w zmiennej env, nigdy w repo. Poniżej: architektura, przykładowy workflow, kod webhooka i obsługa awarii CRM.
Dla kogo to jest
- Stron firmowych z formularzem, który ma trafić do HubSpot / Pipedrive / Notion CRM
- Zespołów bez budżetu na Zapier Enterprise — n8n self-hosted lub fair pricing
- Developerów Next.js chcących odseparować integracje od kodu aplikacji
- Firm B2B, gdzie sales musi dostać Slack w 30 sekund po leadzie
- Projektów z Sentry — webhook fail musi być widoczny
Fraza (SEO)
n8n formularz nextjs, webhook crm automatyzacja, n8n slack lead, server action webhook, integracja formularz hubspot n8n 2026
Architektura — podział odpowiedzialności
[Użytkownik] → [Next.js Server Action]
├→ INSERT lead → PostgreSQL (Branchly)
└→ POST webhook → [n8n]
├→ CRM (HubSpot / Pipedrive)
├→ Slack #sales
├→ Email potwierdzenie (opcjonalnie)
└→ Error branch → Slack #dev-alerts| Warstwa | Odpowiedzialność | Dlaczego tu |
|---|---|---|
| Next.js | Walidacja, CSRF, rate limit, zapis DB | Bezpieczeństwo i source of truth |
| Branchly | PostgreSQL, backup leadów | Gdy CRM padnie, lead nie ginie |
| n8n | Integracje, mapowanie pól, retry | Zmiana CRM bez redeploy aplikacji |
| DevStudioIT Cloud | Runtime Next.js, env secrets | Webhook URL tylko w panelu |
Nie rób całej logiki CRM w Server Action — każda zmiana pola w HubSpot nie powinna wymagać PR w Next.js.
Server Action — zapis + webhook do n8n
'use server';
import { z } from 'zod';
import { db } from '@/lib/db';
import { headers } from 'next/headers';
const contactSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
message: z.string().min(10).max(5000),
company: z.string().max(200).optional(),
});
export async function submitContact(data: unknown) {
const parsed = contactSchema.safeParse(data);
if (!parsed.success) {
return { ok: false, errors: parsed.error.flatten() };
}
const lead = await db.contactLead.create({
data: { ...parsed.data, source: 'website', status: 'new' },
});
const webhookUrl = process.env.N8N_CONTACT_WEBHOOK_URL;
if (!webhookUrl) {
return { ok: true, id: lead.id }; // lead saved, integracja wyłączona
}
const h = await headers();
const payload = {
leadId: lead.id,
...parsed.data,
locale: h.get('x-locale') ?? 'pl',
submittedAt: new Date().toISOString(),
};
// fire-and-forget — nie blokuj UX na CRM
fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Secret': process.env.N8N_WEBHOOK_SECRET ?? '',
},
body: JSON.stringify(payload),
}).catch((err) => {
console.error('n8n webhook failed', { leadId: lead.id, err });
// Sentry.captureException w produkcji
});
return { ok: true, id: lead.id };
}| Decyzja | Uzasadnienie |
|---|---|
| Zapis DB przed webhook | Lead over CRM availability |
fetch bez await w UX path |
Użytkownik dostaje sukces w <300 ms |
| Secret w headerze | n8n weryfikuje, nie każdy może POSTować |
leadId w payload |
Idempotency i replay z Branchly |
n8n workflow — webhook → CRM → Slack
Kroki w n8n (UI):
- Webhook — method POST, path
/contact-lead, authentication Header Auth (X-Webhook-Secret) - IF — walidacja
emailregex (druga linia obrony) - HubSpot / HTTP Request do Pipedrive API — create contact + deal
- Slack — channel
#sales, message blocks z name, email, company, link do CRM - Error Trigger — on fail → Slack
#dev-alerts+ opcjonalnie retry node
Przykład mapowania Slack (expression n8n):
Nowy lead ze strony {{ $json.locale }}:
• {{ $json.name }} ({{ $json.company }})
• {{ $json.email }}
• Lead ID: {{ $json.leadId }}| Node | Retry | Uwagi |
|---|---|---|
| Webhook | — | Entry point |
| CRM create | 3× exponential | Rate limit API |
| Slack | 2× | Token workspace w credentials |
| Email (SMTP) | 1× | Opcjonalny auto-reply |
Workflow eksportuj jako JSON w repo infra/n8n/ — wersjonowanie obok kodu aplikacji.
Bezpieczeńst webhooka
| Zagrożenie | Mitigacja |
|---|---|
| Spam POST na webhook | Secret header + rate limit w Next.js |
| Replay attack | Timestamp + HMAC (opcjonalnie) |
| PII w logach n8n | Wyłącz execution data retention lub scrub |
| Publiczny n8n bez auth | Nigdy — zawsze Header Auth lub Basic |
Webhook URL z n8n.cloud lub self-hosted za reverse proxy z TLS. Env N8N_CONTACT_WEBHOOK_URL ustaw w DevStudioIT Cloud — osobno staging i production.
Awaria CRM — lead nie może zginąć
Scenariusz: HubSpot API 503.
- Lead jest w Branchly (
status: new) - n8n Error Trigger loguje failure
- Cron w n8n co 15 min: HTTP Request do wewnętrznego API Next.js
/api/leads/retry?status=newlub bezpośredni SELECT z DB (credentials Branchly read-only w n8n)
Alternatywa prostsza: manual replay z panelu n8n z leadId — sales dostaje Slack „CRM sync failed, lead #1234 in DB".
Integracja z monitoringiem Sentry: fetch catch + Sentry.captureException gdy webhook timeout >5 s.
Staging — testowanie flow bez spamowania sales
| Środowisko | Webhook URL | Slack channel |
|---|---|---|
| Local | n8n test workflow off / mock | — |
| Staging | N8N_CONTACT_WEBHOOK_URL_STAGING |
#dev-test |
| Production | produkcyjny workflow | #sales |
Branchly branch staging — leady testowe nie w CRM produkcyjnym. n8n duplicate workflow z prefiksem [STAGING].
n8n vs hardcoded integracje w Next.js
| Kryterium | n8n | Kod w Server Action |
|---|---|---|
| Zmiana mapowania CRM | Edycja workflow | PR + deploy |
| Retry / error branch | Wbudowane | Własny kod |
| Audyt kto co zmienił | Workflow history | Git |
| Koszt utrzymania | Instancja n8n | Czas dev |
| Latency | +100–300 ms async | Zależy |
Dla strony firmowej z 1–2 formularzami n8n to sweet spot między Zapier a pełnym custom backend.
Rozszerzenie flow — lead scoring i tagi UTM
Gdy formularz zbiera UTM z sesji (kampania Google Ads), n8n może w jednym workflow:
- Odczytać
utm_source,utm_campaignz payloadu Next.js - Ustawić tag w CRM („Google Ads — Q3 brand")
- Obniżyć priorytet Slack jeśli
utm_source=newsletter(mniej pilne niż direct)
Next.js dołącza UTM z cookie lub hidden fields — waliduj w Zod, nie ufaj ślepo query string. Szczegóły trackingu: UTM i GA4.
| Pole w payload | Źródło | n8n node |
|---|---|---|
utm_campaign |
Server Action z cookie | Set field w CRM |
locale |
header / path | Routing Slack channel per region |
leadId |
Branchly UUID | Idempotency key |
FAQ
Czy await webhook w Server Action jest OK?
Tylko jeśli CRM musi potwierdzić przed komunikatem sukcesu — rzadko. B2B formularz: sukces po zapisie DB; CRM async. Użytkownik nie powinien czekać na HubSpot.
Self-hosted n8n gdzie hostować?
Osobna mała VM obok lub integracja n8n.cloud. Nie na tym samym procesie co Next.js produkcyjny — izolacja zasobów. DevStudioIT Cloud = aplikacja; n8n = osobna usługa.
GDPR — przetwarzanie w n8n?
Umowa powierzenia z operatorem n8n (EU region). Minimalizuj pola w payload — nie wysyłaj message do Slack jeśli zawiera wrażliwe dane; tylko link do CRM.
Make (Integromat) zamiast n8n?
Make działa podobnie; n8n wygrywa przy self-host i braku limitu operacji na własnej instancji. Wybór narzędzia mniej ważny niż pattern: DB first, webhook async.
Chcesz automatyzację formularzy z n8n?
- Skontaktuj się z nami — zaprojektujemy flow Next.js → Branchly → n8n → CRM → Slack
- Monitoring błędów Sentry — alert gdy webhook padnie
- DevStudioIT Cloud + Branchly — hosting aplikacji i baza leadów
O autorze
Budujemy szybkie strony WWW, aplikacje web/mobile, chatboty AI i hosting — z naciskiem na SEO i konwersję.
Przydatne linki
Od teorii do produkcji — Branchly, hosting i realizacje.
