Quick answer: a webhook in n8n is a URL that starts a workflow the instant another app sends it data. You add a Webhook node as your trigger, n8n gives you a unique URL, you paste that URL into the sending app, and every request to it fires your workflow with the request body available as {{ $json }}. The two things that trip people up are the test-vs-production URL split and forgetting to respond to services that expect a reply. This guide covers the whole path — setup, security, parsing, and the patterns we use on real client builds.
How the Webhook node works
The Webhook node is a trigger — it sits at the start of a workflow and listens for incoming HTTP requests. When you add one, n8n generates a URL ending in a unique path (you can customize the path). Any app that can send an HTTP request — a form tool, a payment processor, a CRM, another automation — can call that URL, and the request payload becomes the workflow's input data. You configure the HTTP method it accepts (GET, POST, PUT, etc.), and optionally the authentication it requires before it will run.
Because it's HTTP-native, the Webhook node is the universal connector: even when n8n has no dedicated integration for an app, if that app can fire a webhook (most can), you can trigger n8n from it. That's why webhooks are the backbone of so many real automations — they're the escape hatch that connects the tools that don't otherwise talk.
Test URL vs. production URL — the #1 gotcha
Every Webhook node has two URLs, and confusing them is the most common n8n webhook problem. The Test URL only works while you have the workflow open in the editor and have clicked "Listen for test event" — it's for building and debugging, and it captures one request so you can see the incoming data. The Production URL only works when the workflow is activated (the toggle in the top-right). If you paste the test URL into your live app, it will silently stop working the moment you close the editor.
- Building/testing: use the Test URL, click "Listen for test event," trigger the source app once, and n8n captures the payload for you to map.
- Going live: switch to the Production URL, activate the workflow, and update the sending app to point at the production URL.
- Symptom of getting it wrong: "it worked when I was testing but stopped in production" — almost always the test URL left in a live integration.
Securing your webhooks
An open webhook URL will run for anyone who finds it, so secure it. n8n's Webhook node supports Header Auth, Basic Auth, and JWT out of the box — pick one and require it, so only requests carrying the right credential trigger the workflow. For services that sign their payloads (Stripe, GitHub, and many others send an HMAC signature header), verify that signature inside the workflow before trusting the data: compute the expected signature from the raw body and your secret, and compare. Never act on an unauthenticated webhook that moves money, changes records, or sends messages.
Two more hardening habits: keep the URL path non-guessable (n8n's default random path helps), and validate the payload shape with an IF node early in the workflow so malformed or unexpected requests exit cleanly instead of erroring deep in the flow.
Parsing incoming data
Once a request arrives, its contents are available through n8n expressions. The JSON body is {{ $json }} — so a field like the sender's email is {{ $json.email }} or {{ $json.body.email }} depending on how the payload is nested. Headers and query parameters are available too ({{ $json.headers }}, {{ $json.query }}). The cleanest way to see exactly what you received is the test-event capture: trigger once, expand the Webhook node's output, and click the fields to copy their exact expression paths rather than guessing.
// Example: a form posts {"name":"Sam","email":"sam@acme.com"}
// In a later node, reference the fields with expressions:
Name: {{ $json.name }}
Email: {{ $json.email }}
// Nested payloads (common with CRMs):
{{ $json.body.contact.email }}
Responding to the caller
Many services expect an HTTP response — a form tool wants a 200 to know the submission succeeded; an API caller may want data back. By default the Webhook node responds immediately with a generic 200. For control, set the node's Respond option to "Using Respond to Webhook Node," then add a Respond to Webhook node at the point in your flow where you want to reply — this lets you return custom status codes, headers, or a JSON body (for example, an ID you just created). Use this for synchronous integrations; for fire-and-forget triggers, the default immediate response is fine and keeps the caller from waiting on your whole workflow.
A real pattern: lead capture from any form
Here's a webhook workflow we build constantly. A website form posts to the n8n Production URL → an IF node validates that email and name are present → the lead is written to the CRM (create-or-update) → a Slack or SMS notification fires to the sales team → a Respond to Webhook node returns a 200 so the form shows its success message. It's five nodes, it runs in under a second, and it connects a form to a CRM that have no native integration — the essence of what webhooks make possible. For the automation-engine economics behind running these at volume, see our n8n pricing guide.
Troubleshooting the most common webhook problems
When a webhook misbehaves, the cause is almost always one of a handful of things. Nothing happens when the app sends a request: the workflow isn't activated (production URL is dead until it is), or the app is pointed at the test URL. The workflow runs but the data is empty or wrong: the payload is nested deeper than you referenced — capture a real test event and click the fields to get the exact expression path. It works intermittently: the sending app may be retrying or sending duplicate events, so make the workflow idempotent (an upsert instead of a blind create). You get a 404 on the URL: the path is wrong or the workflow was renamed/deactivated. Walking these four in order resolves the vast majority of webhook issues in minutes.
One subtler gotcha: some services require you to confirm a webhook subscription by echoing back a challenge token they send on setup (a verification handshake). If an app says your webhook "couldn't be verified," add a branch that detects the verification request and responds with the expected token via a Respond to Webhook node — then normal events will flow.
Can one n8n webhook handle multiple event types?
Yes — a single webhook URL can receive many event types, and you branch on them inside the workflow. Read a field in the payload that identifies the event (most services include an "event" or "type" field), then use a Switch node to route each type to the right handling logic. This is cleaner than a separate webhook per event when they come from the same source.
Do webhooks count as executions in n8n's pricing?
Each time a webhook triggers your workflow, that's one execution — the same as any other trigger. Since n8n bills per workflow run (not per step), a webhook firing a 30-node workflow is still one execution. This is why n8n stays affordable for webhook-heavy, multi-step automations where per-task tools would multiply the cost. See our n8n pricing guide for the full math.
FAQ
Why does my n8n webhook work in test but not production?
You're almost certainly using the Test URL in your live app. The Test URL only works while the editor is open and listening; the Production URL only works when the workflow is activated. Switch your app to the Production URL and toggle the workflow active — that fixes it in the vast majority of cases.
How do I secure an n8n webhook?
Enable authentication on the Webhook node (Header Auth, Basic Auth, or JWT) so only credentialed requests trigger it, and for signed sources (Stripe, GitHub) verify the HMAC signature inside the workflow before trusting the payload. Keep the URL path non-guessable and validate the payload shape with an early IF node. Never act on an unauthenticated webhook that moves money or changes data.
Can n8n receive webhooks on the free self-hosted version?
Yes — webhooks work fully on self-hosted n8n (Community Edition), which is free. The only requirement is that your n8n instance is reachable from the internet at a stable URL (a domain or public IP with HTTPS), so the sending apps can reach the production webhook URL. On n8n Cloud this is handled for you.
How do I send data back in the webhook response?
Set the Webhook node's Respond option to "Using Respond to Webhook Node," then place a Respond to Webhook node where you want to reply. It lets you return a custom status code, headers, and JSON body — useful when the caller expects data back (like the ID of a record you just created) rather than a generic 200.
What's the difference between a webhook and a polling trigger?
A webhook is pushed to you the instant an event happens — real-time and efficient. A polling trigger (like "check for new rows every 5 minutes") repeatedly asks an app whether anything changed, which adds latency and consumes executions. Prefer webhooks when the source app supports them; use polling only when it doesn't.
Want these built and owned for you? We deploy n8n (self-hosted, so you own it) and wire the webhook workflows into your stack — get a free automation audit. Next in this series: n8n error handling and building AI agents in n8n.
