Skip to main content
Lifetime license included with every purchase
n8n webhooksevent automationwebhook routingrealtime automation

How to Build Real-Time Webhook Workflows with n8n (Trigger, Route, and Respond to Any Event)

Build n8n webhook workflows that capture any real-time event, validate it, route it to the right action, and respond in seconds — no polling required.

Nn8n Marketplace Team·May 21, 2026·9 min read

Polling Is Dead. Webhooks Are How Events Actually Happen.

Schedule triggers are useful for batch jobs. But for anything time-sensitive — a payment completed, a form submitted, a GitHub push, a support ticket opened — checking every few minutes is the wrong architecture.

Webhooks let the event source push data to your workflow the instant something happens. No polling lag, no wasted API calls, no missed events between check intervals.

n8n's Webhook node turns any HTTP POST into a workflow trigger. One endpoint handles Stripe payment events. Another routes GitHub CI failures. Another processes Typeform submissions — all in real time, all automated.

What You Can Automate With Webhooks

Webhooks work wherever an external system can fire an HTTP request:

  • Payment events — Stripe payment_intent.succeeded, charge.failed, customer.subscription.deleted
  • Form submissions — Typeform, Tally, Webflow, or any form tool that POSTs on submit
  • Git events — GitHub or GitLab push, pull request opened, CI run failed
  • Support tickets — new ticket in Zendesk, Intercom, or Freshdesk
  • E-commerce events — Shopify order created, inventory low, refund requested
  • Auth events — new user signed up via Auth0, Clerk, or Supabase
  • Inbound messages — Slack slash commands, Telegram bot messages, SMS from Twilio
  • Custom product events — any event your app backend fires via a single HTTP call

The Webhook Pipeline

A production-grade n8n webhook workflow follows this shape:

Webhook Trigger (POST /webhook/<id>)
  → Code (validate signature, parse + normalize payload)
  → Switch (route by event_type)
    → payment_succeeded:    Sheets (log) → Gmail (receipt) → Slack (alert)
    → payment_failed:       Gmail (dunning email) → Slack (urgent alert)
    → subscription_deleted: Gmail (offboarding) → CRM (update status)
    → unknown_event:        HTTP Request (log to dead-letter endpoint)
  → Respond to Webhook (200 OK)

Every event arrives at a single endpoint, gets validated and parsed, gets routed to the right action, and gets acknowledged — all within a few hundred milliseconds.

1. Collect — Expose the Webhook Endpoint

The Webhook node gives you an instant HTTPS endpoint. Set the HTTP method to POST and copy the production URL from the node settings panel.

Paste that URL into Stripe's webhook dashboard, Typeform's notification settings, GitHub's repository hooks, or anywhere that accepts a webhook target.

The node passes the full request to the next node: body, headers, query params, and raw body (important for signature verification).

2. Process — Validate and Normalize

Before routing, a Code node does two things.

Signature check — most services sign the payload so you can verify it's genuine:

const crypto = require("crypto");
const secret = "YOUR_WEBHOOK_SECRET";
const signature = $input.first().headers["stripe-signature"];
const rawBody = JSON.stringify($input.first().body);

const expected = crypto
  .createHmac("sha256", secret)
  .update(rawBody)
  .digest("hex");

if (!signature.includes(expected)) {
  throw new Error("Invalid webhook signature — request rejected");
}

return $input.all();

Normalization — extract the fields every downstream node needs:

const body = $json.body;
return [{
  json: {
    event_type:     body.type,
    event_id:       body.id,
    customer_email: body.data?.object?.customer_email ?? "",
    amount_cents:   body.data?.object?.amount ?? 0,
    currency:       body.data?.object?.currency ?? "usd",
    created_at:     new Date(body.created * 1000).toISOString()
  }
}];
Always validate the signature first

An open webhook endpoint with no signature check will process any POST request anyone sends it. Validate the HMAC before running any business logic. A single throw in the Code node stops the workflow and returns a non-200 to the caller — which is exactly what you want on a forged request.

3. Route — Branch by Event Type

A Switch node reads event_type from the normalized payload and routes to the correct branch.

Keep routing logic in the Switch node — not in nested IFs inside a Code node. One condition per branch, one branch per event type. Unknown event types go to a dead-letter branch that logs the raw payload for debugging.

Switch (event_type)
  → "payment_intent.succeeded"      → payment success branch
  → "payment_intent.payment_failed" → payment failure branch
  → "customer.subscription.deleted" → cancellation branch
  → default                         → HTTP Request (POST /logs/unhandled-events)

4. Act — Execute the Right Response

Each branch runs its own action sequence. A payment success branch might:

  1. Google Sheets — append a row to the revenue log
  2. Gmail — send the customer a payment receipt
  3. Slack — post a payment alert to #payments
  4. HTTP Request — call your CRM API to tag the contact as paying

A payment failure branch does different things:

  1. Gmail — send a dunning email with a payment retry link
  2. Slack — post an urgent alert to #billing-alerts
  3. Google Sheets — log the failure with retry count for follow-up tracking
Keep branch actions short and sequential

Webhook workflows need to complete fast. If a branch needs to process many records, write the job to a queue — a Sheets row or a database entry — and let a separate Schedule Trigger pick it up. The webhook workflow's job is to receive, validate, and hand off, not to do heavy processing inline.

Get the Smart Subscription Manager template for Stripe webhook automation

5. Follow Up — Acknowledge the Sender

The final node in every branch is a Respond to Webhook node.

Stripe requires a 200 OK within 30 seconds or it retries the event. Slack slash commands require a response within 3 seconds or they show a timeout error.

{
  "received": true,
  "event": "payment_intent.succeeded"
}

Set the HTTP status code to 200. If your workflow errored, n8n returns a non-200 automatically — the sender retries, and the failure appears in your execution log.

Test with ngrok before going to production

During development, expose your local n8n instance with ngrok. Run ngrok http 5678, copy the HTTPS URL, and paste it as the webhook target in your service's test mode. Trigger test events and inspect the raw payload before building the routing logic. This is faster than deploying to a cloud instance for every iteration.

Implementation Patterns

Pattern 1 — Multi-Event Single Endpoint

One endpoint handles all event types from a single service:

Webhook (POST /stripe-events)
  → Code (verify signature, extract event_type)
  → Switch (event_type)
    → payment_succeeded:        log + receipt email + Slack alert
    → payment_failed:           dunning email + urgent Slack alert
    → subscription_created:     welcome sequence trigger
    → subscription_deleted:     offboarding sequence trigger
    → invoice_upcoming:         payment reminder email
    → default:                  log to unhandled-events sheet

One URL in Stripe's dashboard. All events flow through one validation step. Adding a new event type means adding one Switch branch — nothing else changes.

Pattern 2 — Cross-Service Fan-Out

One incoming event triggers actions across multiple services simultaneously:

Webhook (POST /new-signup — from your product backend)
  → Code (parse: email, plan, source)
  → [parallel branches]
    → Branch A: HTTP Request (create HubSpot contact)
    → Branch B: Gmail (send welcome email)
    → Branch C: Slack (alert #sales with plan and source)
    → Branch D: Google Sheets (log to onboarding tracker)
  → Respond to Webhook (200 OK)

n8n runs the parallel branches concurrently. All four actions complete before the final response node fires.

Pattern 3 — Webhook-to-Queue for Heavy Processing

When the event triggers work that takes more than a few seconds:

Webhook (POST /export-requested)
  → Code (extract: user_id, export_type, filters)
  → Google Sheets (append row: status=pending, requested_at=now)
  → Respond to Webhook (202 Accepted)

[Separate workflow — Schedule Trigger every 2 minutes]
  → Sheets (rows where status=pending)
  → HTTP Request (call export API — may take 5-30s per row)
  → Code (build download link from response)
  → Gmail (send download link to user)
  → Sheets (update row: status=complete)

The webhook responds immediately with 202 Accepted. The heavy work runs in a background workflow that never blocks the HTTP response.

The Data Entry Hub template uses a similar queue pattern — webhooks write to a central sheet, a separate workflow processes each row on a schedule.

n8n Nodes You'll Use Most

NodePurpose
WebhookExpose an HTTPS endpoint; receive body, headers, and query params
Respond to WebhookSend a custom HTTP status and body back to the caller
CodeValidate signatures, normalize payloads, build response bodies
SwitchRoute by event_type or any field in the normalized payload
IFBinary conditions — signature valid/invalid, amount above threshold
HTTP RequestCall external APIs in response to the event
GmailSend event-driven emails: receipts, alerts, dunning
Google SheetsLog events, queue jobs, track processing status
SlackPost real-time alerts to team channels
WaitPause mid-workflow before sending a follow-up message

Getting Started

  1. Pick one event source and one event type — start with Stripe payment_intent.succeeded or a Typeform submission. One service, one event.
  2. Create the Webhook node — set method to POST, copy the production URL.
  3. Register the URL — paste it into the service's webhook settings. Enable test mode if available.
  4. Fire a test event — use the service's "send test" button. Watch the n8n execution log to see the raw payload structure.
  5. Add the Code node — implement signature verification and normalization using the real field paths from the test payload.
  6. Add the Switch node — one branch for the event type you're handling. Add a default branch that logs unknowns to a Google Sheet.
  7. Build the action branch — wire up Sheets, Gmail, Slack, or your CRM. Add a Respond to Webhook node at the end of every branch.

Once the first event type is working end to end, adding branches for additional event types is fast — the validation and routing infrastructure already exists.

For workflows that send emails or Slack messages as part of their response, see n8n email inbox automation and n8n Slack automation. For handling the data that webhook events write to spreadsheets, see the guide to n8n data reporting automation.

If you're building a subscription billing pipeline on top of Stripe webhooks, the Smart Subscription Manager and Email Follow-up Automator templates give you a working starting point with the routing logic already wired up.

Browse real-time event automation templates for n8n
FAQ

Common questions

What's the difference between a webhook trigger and a schedule trigger in n8n?
A schedule trigger runs your workflow on a timer — every hour, every morning, every Monday. A webhook trigger fires the instant an external system sends an HTTP POST to your n8n endpoint. For time-sensitive events like a payment, a form submission, or a GitHub push, webhooks are the right choice: they eliminate polling lag and unnecessary API calls.
How do I validate that a webhook request is genuine in n8n?
Most services sign their webhook payloads with a shared secret. Stripe uses HMAC-SHA256 — the signature arrives in the Stripe-Signature header. A Code node in n8n reads that header, recomputes the HMAC using your webhook secret and the raw request body, and compares the result. If they don't match, the node throws an error and stops the workflow before any business logic runs. GitHub, Shopify, and most services follow the same pattern with their own header names.
Can n8n respond back to the webhook sender within the same request?
Yes. n8n's Webhook node has a 'Respond to Webhook' option that lets you return a custom HTTP status and body before or after the workflow completes. This is essential for Stripe, which expects a 200 OK within 30 seconds, and for Slack slash commands, which need a response within 3 seconds. Set up a Respond to Webhook node at the end of each branch to acknowledge every event.
Stop reading. Start running.

Get the workflow templates this guide is built on

Import-ready n8n JSON, step-by-step setup, and tested end-to-end. One-time payment, own it forever.