Webhooks are the backbone of real-time automation. When a customer places an order, when a payment succeeds, when someone submits a form, when code gets pushed to a repository, these events happen outside n8n. The Webhook node is how you bring those events into your workflows and respond instantly.
Unlike scheduled triggers that poll for data periodically, webhooks push data to your workflow the moment something happens. This makes them essential for time-sensitive automations where delays matter: payment processing, lead capture, alerting systems, and real-time integrations.
The Webhook Configuration Challenge
Setting up webhooks in n8n seems straightforward until it stops working. The workflow looks correct. The external service says it sent the data. But nothing happens. No execution. No error. Just silence.
The problem is usually one of three things: the wrong URL, incorrect authentication, or a misunderstanding of how n8n’s test and production modes work. This guide covers all of these in depth so you can debug webhook issues in minutes, not hours.
What You’ll Learn
- The critical difference between test and production webhook URLs (the #1 source of confusion)
- How to configure authentication to secure your webhooks
- Response handling options for different use cases
- Dynamic path parameters for flexible endpoint routing
- Troubleshooting the most common webhook problems
- Security best practices for production deployments
- Real-world examples you can adapt to your workflows
When to Use the Webhook Node
The Webhook node is a trigger node. It starts your workflow when an external service sends an HTTP request to your n8n instance. Before diving into configuration, understand when webhooks are the right choice versus other trigger options.
| Scenario | Best Trigger | Why |
|---|---|---|
| Stripe payment notification | Webhook | Stripe sends events immediately when payments occur |
| GitHub push/PR events | Webhook | GitHub webhooks provide real-time repository updates |
| Form submission from website | Webhook | Your form can POST directly to the n8n endpoint |
| Process data every hour | Schedule Trigger | No external event, just time-based execution |
| New email arrives | Email Trigger or IMAP | Dedicated email triggers handle authentication |
| Manual workflow run | Manual Trigger | User clicks execute in n8n interface |
| New row in Google Sheets | Google Sheets Trigger | Native trigger polls for changes |
| Custom internal service | Webhook | Your service can call the webhook URL |
Rule of thumb: If an external service can send HTTP requests to a URL when events happen, use a Webhook. If you need to check for changes periodically, use a native app trigger or Schedule Trigger.
Webhook vs HTTP Request Node
New users sometimes confuse these nodes:
- Webhook node: Receives incoming HTTP requests (listens for data from external services)
- HTTP Request node: Sends outgoing HTTP requests (fetches data from external APIs)
Think of it this way: Webhook is the doorbell that starts your workflow. HTTP Request is you going out to get something.
Understanding Webhook URLs
Every Webhook node generates two URLs: a test URL and a production URL. Understanding the difference between these is critical because using the wrong one is the most common webhook mistake.
Test URL vs Production URL
| Aspect | Test URL | Production URL |
|---|---|---|
| URL path | /webhook-test/... | /webhook/... |
| When active | Only while listening in editor | Only when workflow is activated |
| Timeout | 120 seconds | No explicit timeout (server dependent) |
| Data display | Shows in editor for debugging | Visible only in execution log |
| Use case | Development and testing | Production integrations |
Test URL behavior:
- Open your workflow in the n8n editor
- Click “Listen for Test Event” on the Webhook node
- n8n registers a temporary webhook that stays active for 120 seconds
- Send a request to the test URL
- Data appears in the node output panel for inspection
- If no request arrives within 120 seconds, the listener times out
Production URL behavior:
- Configure and save your workflow
- Toggle the workflow to “Active” (switch in top right)
- n8n registers a persistent webhook
- External services can send requests anytime
- Each request triggers a workflow execution
- View executions in the Executions tab
Common URL Mistakes
Mistake 1: Using test URL in production
Wrong: https://your-n8n.com/webhook-test/abc123
Right: https://your-n8n.com/webhook/abc123
The test URL only works when you have the editor open and are actively listening. If you configure Stripe to use a test URL, webhooks fail when you close the browser.
Mistake 2: Workflow not activated
You copied the production URL correctly, but the workflow toggle is off. Production webhooks only work when the workflow is active. Check the toggle in the top right of the editor.
Mistake 3: URL path typo
Webhook paths are case-sensitive. If your path is myWebhook and you call mywebhook, it fails. Double-check the exact URL shown in the node.
Webhook URL Structure
The full webhook URL consists of:
https://[your-n8n-domain]/webhook/[path]
For self-hosted instances, configure the base URL with the WEBHOOK_URL environment variable. For n8n Cloud, the domain is your instance URL.
The path is either auto-generated (a random string) or custom-defined. Custom paths make URLs more readable and meaningful:
Auto-generated: /webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890
Custom path: /webhook/stripe-payments
Custom path: /webhook/github-push
Your First Webhook
Let’s create a working webhook from scratch and test it with a real request.
Step 1: Add the Webhook Node
- Open n8n and create a new workflow
- Click + to add a node
- Search for “Webhook”
- Click to add it as your trigger
The node appears with configuration options on the right panel.
Step 2: Configure Basic Settings
With the Webhook node selected:
- HTTP Method: Leave as POST (most common for webhooks)
- Path: Enter a custom path like
my-first-webhookor leave the auto-generated one - Authentication: Leave as “None” for testing
- Respond: Leave as “Immediately”
You’ll see both the test and production URLs at the top of the panel.
Step 3: Test with curl
Copy the test URL, then click “Listen for Test Event” to start listening.
Open a terminal and send a test request:
curl -X POST https://your-n8n.com/webhook-test/my-first-webhook \
-H "Content-Type: application/json" \
-d '{"name": "Test User", "email": "[email protected]", "amount": 99.99}'
Step 4: Verify the Data
After sending the request, the Webhook node shows the received data:
{
"headers": {
"content-type": "application/json",
"user-agent": "curl/7.88.1"
},
"params": {},
"query": {},
"body": {
"name": "Test User",
"email": "[email protected]",
"amount": 99.99
}
}
You can now access this data in subsequent nodes:
{{ $json.body.name }}returns “Test User”{{ $json.body.email }}returns “[email protected]”{{ $json.body.amount }}returns 99.99
For more on working with data in expressions, see our n8n expressions guide.
Step 5: Activate for Production
Once testing is complete:
- Connect additional nodes to process the data
- Save the workflow
- Toggle the workflow to “Active”
- Use the production URL (without
-test) in your external service
HTTP Methods
The Webhook node supports standard HTTP methods. Each method has conventional uses, though your external service dictates which one to use.
| Method | Conventional Use | Common Webhook Scenarios |
|---|---|---|
| POST | Send data to be processed | Payment events, form submissions, most webhooks |
| GET | Retrieve information | Health checks, verification callbacks |
| PUT | Replace a resource entirely | Full record updates |
| PATCH | Partial update | Incremental changes |
| DELETE | Remove a resource | Deletion notifications |
| HEAD | Headers only (no body) | Connectivity checks |
POST is the default and most common. Nearly all webhook providers (Stripe, GitHub, Shopify, HubSpot) use POST to send event data.
Accepting Multiple HTTP Methods
By default, the Webhook node accepts only the method you select. If a service sends POST but your webhook expects GET, the request fails silently.
To accept multiple methods:
- Open the Webhook node
- Click the gear icon for Settings
- Enable Allow Multiple HTTP Methods
- Select all methods you want to accept
This is useful for services that send different event types with different methods, or when building REST-like endpoints.
Authentication Methods
Public webhooks are security risks. Anyone who discovers your URL can trigger your workflow with fake data. For production deployments, always configure authentication.
The Webhook node supports four authentication methods. Choose based on what your external service supports and your security requirements.
No Authentication
Use only for testing or internal services on private networks.
No configuration needed. Any request to the URL triggers the workflow.
Header Authentication
The most common method for custom integrations. You define a secret header that callers must include.
Configuration:
- Set Authentication to “Header Auth”
- Create credentials with:
- Header Name: e.g.,
X-Webhook-Secret - Header Value: A long random string
- Header Name: e.g.,
Caller must include:
curl -X POST https://your-n8n.com/webhook/my-endpoint \
-H "X-Webhook-Secret: your-secret-value-here" \
-H "Content-Type: application/json" \
-d '{"event": "test"}'
Requests without the correct header receive a 401 Unauthorized response.
Basic Authentication
Standard HTTP basic auth using username and password.
Configuration:
- Set Authentication to “Basic Auth”
- Create credentials with username and password
Caller authenticates with:
curl -X POST https://your-n8n.com/webhook/my-endpoint \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{"event": "test"}'
Basic auth credentials are Base64-encoded (not encrypted), so always use HTTPS.
JWT Authentication
For services that send JSON Web Tokens.
Configuration:
- Set Authentication to “JWT Auth”
- Configure the JWT secret or public key for verification
The Webhook node validates the token signature and optionally checks claims like expiration.
Authentication Comparison
| Method | Security Level | Complexity | Best For |
|---|---|---|---|
| None | Low | Trivial | Testing only |
| Header Auth | Medium | Easy | Custom integrations, internal services |
| Basic Auth | Medium | Easy | Legacy systems |
| JWT Auth | High | Complex | OAuth flows, user-context webhooks |
For payment webhooks and high-value integrations, authentication alone is not enough. You should also verify webhook signatures. See our comprehensive webhook security guide for HMAC verification patterns.
If you run into authentication issues, check our guide to fixing n8n authentication errors.
Response Handling
When a service sends a webhook, it usually expects a response confirming receipt. The Webhook node offers four response modes that control what and when you send back.
Immediately
Returns a response as soon as the webhook is received, before the workflow processes the data.
Response:
HTTP/1.1 200 OK
{"message": "Workflow was started"}
Use when:
- The caller does not need processed data back
- Your workflow takes a long time to complete
- You want to acknowledge receipt quickly (important for timeout-sensitive services)
This is the default and most common mode.
When Last Node Finishes
Waits for the entire workflow to complete, then returns the output of the last executed node.
Response:
HTTP/1.1 200 OK
{"result": "processed", "orderId": "12345", "status": "confirmed"}
Use when:
- Building API-like endpoints that return processed data
- The caller needs the result of your workflow
- Workflow execution is fast enough to avoid timeouts
Caution: Long-running workflows may timeout. n8n Cloud uses Cloudflare, which enforces a 100-second timeout. Self-hosted instances depend on your reverse proxy configuration. For timeout troubleshooting, see our timeout errors guide.
Using Respond to Webhook Node
Provides full control over the response using a separate Respond to Webhook node.
Configuration:
- Set Webhook response to “Using ‘Respond to Webhook’ Node”
- Add a Respond to Webhook node anywhere in your workflow
- Configure the response body, status code, and headers
Example: Custom error response
// In Respond to Webhook node
{
"success": false,
"error": "Invalid order ID",
"code": "INVALID_ORDER"
}
With status code 400 Bad Request.
Use when:
- You need conditional responses (success vs error)
- Different branches should return different data
- You need custom HTTP status codes or headers
Streaming Response
Returns data progressively as the workflow executes. Useful for AI/LLM workflows where you want to stream tokens back to the caller.
Requirements:
- Workflow must contain nodes that support streaming (AI agent nodes)
- Caller must handle streaming responses
Use when:
- Building chat interfaces with LLMs
- Long operations where users benefit from progressive feedback
Path Parameters
Path parameters let you create dynamic webhook endpoints that capture data from the URL itself. Instead of a single static endpoint, you can route different resources through the same webhook.
Supported Path Formats
| Format | Example | Captured Parameters |
|---|---|---|
| Single variable | /:id | id |
| Path with variable | /users/:userId | userId |
| Variable with path | /:tenantId/events | tenantId |
| Multiple variables | /:org/:repo | org, repo |
| Complex path | /api/:version/:resource/:id | version, resource, id |
Configuration
In the Webhook node Path field, include variables prefixed with ::
Path: /orders/:orderId
Full URL: https://your-n8n.com/webhook/orders/12345
When a request hits /webhook/orders/12345, the Webhook node captures:
{
"params": {
"orderId": "12345"
},
"body": { ... }
}
Access the parameter in subsequent nodes:
{{ $json.params.orderId }} // Returns "12345"
Multi-Tenant Example
A common use case is routing webhooks for different customers or tenants:
Path: /:customerId/events
Different customers can use their own endpoints:
/webhook/acme-corp/events/webhook/beta-inc/events/webhook/gamma-llc/events
Your workflow can then route or process differently based on the customer ID.
When to Use Path Parameters
- Resource identification: Capture IDs directly in the URL
- API versioning: Include version in path like
/v1/:resource - Multi-tenant systems: Route different organizations through the same workflow
- RESTful patterns: Build REST-like endpoints with consistent URL structure
For simpler cases, query parameters (?id=123) work just as well and may be easier to configure in external services.
Handling Webhook Payloads
Once data arrives at your webhook, you need to access and process it. The Webhook node provides structured access to all parts of the incoming request.
Accessing Request Data
| Data Location | Expression | Example |
|---|---|---|
| Request body | {{ $json.body }} | Posted JSON data |
| Specific body field | {{ $json.body.fieldName }} | Single value from body |
| Headers | {{ $json.headers }} | All request headers |
| Specific header | {{ $json.headers['x-custom-header'] }} | Single header value |
| Query parameters | {{ $json.query }} | URL query string params |
| Path parameters | {{ $json.params }} | Captured from URL path |
JSON Body Example
If Stripe sends:
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_12345",
"amount": 2000,
"currency": "usd"
}
}
}
Access the data with:
{{ $json.body.type }} // "payment_intent.succeeded"
{{ $json.body.data.object.id }} // "pi_12345"
{{ $json.body.data.object.amount / 100 }} // 20.00 (Stripe amounts are in cents)
For complex expressions, test them first with our expression validator tool.
Headers Example
Headers are lowercase in n8n. Access them with bracket notation:
{{ $json.headers['content-type'] }} // "application/json"
{{ $json.headers['x-webhook-signature'] }} // Signature for verification
{{ $json.headers['user-agent'] }} // Caller identification
Query Parameters
If the URL is called with ?status=active&limit=10:
{{ $json.query.status }} // "active"
{{ $json.query.limit }} // "10" (string, convert if needed)
Binary Data and Files
For file uploads, the Webhook node can receive binary data. Enable binary handling in the node options, and the file becomes available as binary data for use with file processing nodes.
Payload Size Limits
The maximum webhook payload is 16MB by default. For self-hosted instances, adjust with the N8N_PAYLOAD_SIZE_MAX environment variable:
N8N_PAYLOAD_SIZE_MAX=32 # MB
For very large payloads, consider having the sender upload to cloud storage and send only a reference URL.
Common Issues and Troubleshooting
These are the problems that trip up most users, based on patterns from the n8n community forum.
Issue 1: Webhook Not Triggering
Symptoms: External service says it sent the webhook, but no execution appears in n8n.
Diagnosis checklist:
-
Check URL type: Are you using the test URL with the workflow inactive? Switch to production URL and activate the workflow.
-
Check workflow activation: The toggle in the top right must be on for production webhooks.
-
Check URL path: Paths are case-sensitive. Verify the exact URL.
-
Check HTTP method: If your webhook expects POST but the service sends GET, requests fail silently. Enable multiple methods in settings if needed.
-
Check authentication: If authentication is configured, the caller must include valid credentials.
-
Check network/firewall: For self-hosted, ensure your n8n instance is reachable from the internet on the webhook port.
Issue 2: Duplicate Webhook Events
Symptoms: The same event triggers multiple workflow executions.
Common causes:
- Service retry behavior: Many services retry failed webhooks. If your workflow times out or returns an error, the service sends again.
- Multiple workflow activations: Accidentally activating multiple versions of the same workflow.
- Meta/Instagram specific: Facebook platforms are known to send duplicate deliveries; use idempotency keys to deduplicate.
Solutions:
- Return a 200 response quickly using “Immediately” mode
- Implement idempotency checking using an event ID
- For Meta webhooks specifically, check if you’re processing the same message ID twice
Issue 3: Timeout Errors
Symptoms: Webhook returns error or the caller times out.
Causes:
- Workflow takes too long to execute
- n8n Cloud Cloudflare limit: 100 seconds
- Self-hosted reverse proxy timeout
Solutions:
- Use “Immediately” response mode for long workflows
- Process asynchronously and send results via separate HTTP request
- Increase timeout in reverse proxy configuration (self-hosted)
See our timeout troubleshooting guide for detailed solutions.
Issue 4: Authentication Failures
Symptoms: 401 Unauthorized errors when calling webhook.
Diagnosis:
- Verify credentials match exactly (case-sensitive)
- Check header name spelling
- For basic auth, ensure username:password encoding is correct
- Test with curl to isolate whether the issue is n8n or the calling service
Issue 5: Missing Body or Headers
Symptoms: $json.body is empty or undefined.
Causes:
- Content-Type header mismatch (service sending form data, not JSON)
- Body not included in request
- n8n parsing issue
Solutions:
- Check the exact Content-Type the caller is sending
- For form data, access
$json.bodydifferently or parse manually - Log raw request data for debugging
Use our workflow debugger tool to diagnose these issues faster.
Security Best Practices
Webhooks are publicly accessible endpoints. Without proper security, attackers can trigger your workflows with malicious data.
Always Use Authentication in Production
Never deploy production webhooks without authentication. Header auth is the minimum:
# Secure request with header
curl -X POST https://your-n8n.com/webhook/orders \
-H "X-Webhook-Secret: your-long-random-secret" \
-H "Content-Type: application/json" \
-d '{"orderId": 123}'
Generate secrets using cryptographically secure random generators, not simple passwords.
Verify Webhook Signatures
Major platforms (Stripe, GitHub, Shopify) sign webhooks using HMAC. This proves the request came from them and was not tampered with.
n8n does not have built-in HMAC verification, but you can implement it with a Code node:
const crypto = require('crypto');
const payload = JSON.stringify($json.body);
const signature = $json.headers['x-hub-signature-256']; // GitHub example
const secret = $env.GITHUB_WEBHOOK_SECRET;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
return $json;
For complete HMAC verification patterns including Stripe and Shopify, see our webhook security guide.
Validate Input Data
Never trust webhook data blindly:
- Check required fields exist before processing
- Validate data types (is
amountactually a number?) - Sanitize strings that will be used in database queries or displayed
Use HTTPS
Always use HTTPS for webhook endpoints. HTTP transmits credentials and data in plain text.
For self-hosted n8n, configure SSL termination at your reverse proxy (nginx, Caddy, Traefik).
Rate Limiting
Implement rate limiting at the reverse proxy level to prevent abuse:
limit_req_zone $binary_remote_addr zone=webhook_limit:10m rate=10r/s;
location /webhook/ {
limit_req zone=webhook_limit burst=20 nodelay;
proxy_pass http://localhost:5678;
}
This limits each IP to 10 requests per second with a burst allowance of 20.
For comprehensive security implementation, see our webhook security guide or explore our consulting services for professional security audits.
Real-World Examples
Example 1: Stripe Payment Webhook
Scenario: Process successful payments from Stripe.
Method: POST
Path: /stripe-payments
Authentication: None (use signature verification instead)
Response: Immediately
Stripe webhook payload structure:
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_12345",
"amount": 5000,
"customer": "cus_abc123"
}
}
}
Workflow pattern:
- Webhook receives event
- Code node verifies Stripe signature
- IF node checks
$json.body.type === "payment_intent.succeeded" - Downstream nodes process the payment (update database, send receipt, etc.)
Example 2: GitHub Push Webhook
Scenario: Trigger deployment when code is pushed to main branch.
Method: POST
Path: /github-deploy
Authentication: Header Auth (X-Hub-Signature-256)
Response: Immediately
Key payload fields:
{{ $json.body.ref }} // "refs/heads/main"
{{ $json.body.repository.name }} // "my-repo"
{{ $json.body.pusher.name }} // "developer-name"
Workflow pattern:
- Webhook receives push event
- IF node checks
$json.body.ref === "refs/heads/main" - Execute Command node runs deployment script
- Slack node notifies team of deployment status
Example 3: Custom Form Handler
Scenario: Receive form submissions from your website.
Method: POST
Path: /contact-form
Authentication: Header Auth
Response: When Last Node Finishes (return confirmation)
Form posts:
{
"name": "Jane Doe",
"email": "[email protected]",
"message": "Interested in your services",
"source": "website-footer"
}
Workflow pattern:
- Webhook receives submission
- Set node formats data
- Google Sheets node logs the lead
- Email node sends to sales team
- Respond to Webhook returns confirmation
Example 4: Chatbot Integration
Scenario: Receive messages from a chat platform (Slack, Discord, custom).
Method: POST
Path: /chatbot
Authentication: Header Auth
Response: Using Respond to Webhook Node
Workflow pattern:
- Webhook receives message
- AI agent processes the message
- Respond to Webhook returns the AI response
For chat integrations, response time matters. Use streaming responses for AI workflows where supported.
Example 5: CRM Update Notifications
Scenario: HubSpot notifies you when a deal stage changes.
Method: POST
Path: /hubspot-deals
Authentication: None (HubSpot does not support custom auth; verify source differently)
Response: Immediately
Workflow pattern:
- Webhook receives deal update
- IF node checks deal stage and value
- High-value deals trigger Slack alert to sales manager
- All updates logged to analytics database
Pro Tips and Best Practices
1. Use Descriptive Webhook Paths
Instead of /webhook/a1b2c3d4, use meaningful paths:
/webhook/stripe-payments
/webhook/github-deploys
/webhook/contact-submissions
This makes debugging easier and URLs more readable in external service configurations.
2. Log Incoming Data During Development
Add a Set node after the webhook to capture raw data:
{
"timestamp": "{{ $now }}",
"headers": {{ JSON.stringify($json.headers) }},
"body": {{ JSON.stringify($json.body) }},
"params": {{ JSON.stringify($json.params) }}
}
Send this to a Google Sheet or database during development to debug payload issues.
3. Always Acknowledge Receipt
External services retry failed webhooks. Return a 200 quickly to prevent duplicate processing:
- Use “Immediately” response mode for long workflows
- Process heavy logic asynchronously if needed
4. Handle Idempotency
Many services include an event ID. Store processed IDs and skip duplicates:
const eventId = $json.body.id;
// Check database if eventId was already processed
// Skip if duplicate
5. Monitor Webhook Health
Track webhook executions in n8n’s Executions tab. Look for:
- Failure patterns (same error recurring)
- Unusual volume spikes (possible abuse)
- Timeout trends (workflow too slow)
Set up an Error Trigger workflow to alert you when webhooks fail. For monitoring setup, see our workflow best practices guide.
6. Document Your Endpoints
Keep a record of:
- Each webhook path and what service uses it
- Expected payload structure
- Authentication credentials location
- Workflow it triggers
This saves hours when debugging or onboarding team members.
For professional help building robust webhook integrations, explore our workflow development services.
Frequently Asked Questions
Why is my webhook not triggering even though I set up the URL correctly?
The most common cause is using the test URL instead of the production URL, or having the workflow deactivated. Test URLs (containing /webhook-test/) only work while you have the n8n editor open and are actively clicking “Listen for Test Event.” For webhooks to work continuously, you must use the production URL (containing /webhook/) and toggle the workflow to Active. Also verify the HTTP method matches what the external service sends. If your webhook expects POST but receives GET, the request fails silently. Enable “Allow Multiple HTTP Methods” in settings if you are unsure which method the service uses.
What is the difference between the Test URL and Production URL?
Test URLs are temporary endpoints that only work during active development in the n8n editor. When you click “Listen for Test Event,” n8n creates a test webhook that stays active for 120 seconds, letting you send test requests and see the data in the editor. Production URLs are permanent endpoints that work when your workflow is activated. They persist even when the editor is closed and are what you should configure in external services like Stripe, GitHub, or Shopify. The URL paths differ slightly: test URLs contain /webhook-test/ while production URLs contain /webhook/.
How do I secure my webhook to prevent unauthorized access?
Never use “None” authentication in production. At minimum, configure Header Auth with a strong secret that callers must include in their requests. For webhooks from major platforms (Stripe, GitHub, Shopify), implement HMAC signature verification using a Code node to cryptographically prove the request came from the legitimate source and was not tampered with. Additional security layers include rate limiting at your reverse proxy, IP allowlisting if the sender publishes their IP ranges, and input validation before processing any data. See our comprehensive webhook security guide for implementation details.
Why am I receiving duplicate webhook events?
Duplicate events typically occur because the sending service retries webhooks when it does not receive a timely 200 response. If your workflow takes too long and times out (n8n Cloud has a 100-second Cloudflare limit), the service assumes delivery failed and sends again. To fix this, use “Immediately” response mode to acknowledge receipt quickly, then process data asynchronously if needed. For platforms like Meta/Instagram that send inherent duplicates, implement idempotency checking by storing processed event IDs and skipping any you have already handled. Also verify you do not have multiple workflows listening on the same webhook path.
How do I respond with custom data or status codes to the webhook caller?
To send custom responses, set the Response mode to “Using ‘Respond to Webhook’ Node” in your Webhook configuration. Then add a Respond to Webhook node anywhere in your workflow after the processing logic. In that node, you can configure the response body (JSON, text, or binary), HTTP status code (200, 201, 400, 500, etc.), and custom headers. This is useful for building API-like endpoints that return processed data, or for returning structured error responses when validation fails. For simple confirmations, “When Last Node Finishes” mode automatically returns the last node’s output without needing a separate response node.