Files
gh-czlonkowski-n8n-skills/skills/n8n-workflow-patterns/webhook_processing.md
2025-11-29 18:17:17 +08:00

12 KiB

Webhook Processing Pattern

Use Case: Receive HTTP requests from external systems and process them instantly.


Pattern Structure

Webhook → [Validate] → [Transform] → [Action] → [Response/Notify]

Key Characteristic: Instant event-driven processing


Core Components

1. Webhook Node (Trigger)

Purpose: Create HTTP endpoint to receive data

Configuration:

{
  path: "form-submit",        // URL path: https://n8n.example.com/webhook/form-submit
  httpMethod: "POST",         // GET, POST, PUT, DELETE
  responseMode: "onReceived", // or "lastNode" for custom response
  responseData: "allEntries"  // or "firstEntryJson"
}

Critical Gotcha: Data is nested under $json.body

 {{$json.email}}
 {{$json.body.email}}

Purpose: Verify incoming data before processing

Options:

  • IF node - Check required fields exist
  • Code node - Custom validation logic
  • Stop and Error - Fail gracefully with message

Example:

// IF node condition
{{$json.body.email}} is not empty AND
{{$json.body.name}} is not empty

3. Transformation

Purpose: Map webhook data to desired format

Typical nodes:

  • Set - Field mapping
  • Code - Complex transformations

Example (Set node):

{
  "user_email": "={{$json.body.email}}",
  "user_name": "={{$json.body.name}}",
  "timestamp": "={{$now}}"
}

4. Action

Purpose: Do something with the data

Common actions:

  • Store in database (Postgres, MySQL, MongoDB)
  • Send notification (Slack, Email, Discord)
  • Call another API (HTTP Request)
  • Update external system (CRM, support ticket)

5. Response (If responseMode: "lastNode")

Purpose: Send custom HTTP response

Webhook Response Node:

{
  statusCode: 200,
  headers: {
    "Content-Type": "application/json"
  },
  body: {
    "status": "success",
    "message": "Form received"
  }
}

Common Use Cases

1. Form Submissions

Flow: Form → Webhook → Validate → Database → Email Confirmation

Example:

1. Webhook (path: "contact-form", POST)
2. IF (check email & message not empty)
3. Postgres (insert into contacts table)
4. Email (send confirmation to user)
5. Slack (notify team in #leads)
6. Webhook Response ({"status": "success"})

Real Data Access:

Name: {{$json.body.name}}
Email: {{$json.body.email}}
Message: {{$json.body.message}}

2. Payment Webhooks (Stripe, PayPal)

Flow: Payment Provider → Webhook → Verify → Update Database → Send Receipt

Security: Verify webhook signatures

// Code node - verify Stripe signature
const crypto = require('crypto');
const signature = $input.item.headers['stripe-signature'];
const secret = $credentials.stripeWebhookSecret;

// Verify signature matches
const expectedSig = crypto
  .createHmac('sha256', secret)
  .update($input.item.body)
  .digest('hex');

if (signature !== expectedSig) {
  throw new Error('Invalid webhook signature');
}

return $input.item.body; // Return validated body

3. Chat Platform Integrations (Slack, Discord, Teams)

Flow: Chat Command → Webhook → Process → Respond

Example (Slack slash command):

1. Webhook (path: "slack-command", POST)
2. Code (parse Slack payload: $json.body.text, $json.body.user_id)
3. HTTP Request (fetch data from API)
4. Set (format Slack message)
5. Webhook Response (immediate Slack response)

Slack Data Access:

Command: {{$json.body.command}}
Text: {{$json.body.text}}
User ID: {{$json.body.user_id}}
Channel ID: {{$json.body.channel_id}}

4. GitHub/GitLab Webhooks

Flow: Git Event → Webhook → Parse → Notify/Deploy

Example (new PR notification):

1. Webhook (path: "github", POST)
2. IF (check $json.body.action equals "opened")
3. Set (extract PR details: title, author, url)
4. Slack (notify #dev-team)
5. Webhook Response (200 OK)

GitHub Data Access:

Event Type: {{$json.headers['x-github-event']}}
Action: {{$json.body.action}}
PR Title: {{$json.body.pull_request.title}}
Author: {{$json.body.pull_request.user.login}}
URL: {{$json.body.pull_request.html_url}}

5. IoT Device Data

Flow: Device → Webhook → Validate → Store → Alert (if threshold)

Example (temperature sensor):

1. Webhook (path: "sensor-data", POST)
2. Set (extract sensor readings)
3. Postgres (insert into sensor_readings)
4. IF (temperature > 80)
5. Email (alert admin)

Webhook Data Structure

Standard Structure

{
  "headers": {
    "content-type": "application/json",
    "user-agent": "...",
    "x-custom-header": "..."
  },
  "params": {
    "id": "123"  // From URL: /webhook/form/:id
  },
  "query": {
    "token": "abc"  // From URL: /webhook/form?token=abc
  },
  "body": {
    // ⚠️ YOUR DATA IS HERE!
    "name": "John",
    "email": "john@example.com"
  }
}

Accessing Different Parts

// Headers
{{$json.headers['content-type']}}
{{$json.headers['x-api-key']}}

// URL Parameters
{{$json.params.id}}

// Query Parameters
{{$json.query.token}}
{{$json.query.page}}

// Body (MOST COMMON)
{{$json.body.email}}
{{$json.body.user.name}}
{{$json.body.items[0].price}}

Authentication & Security

1. Query Parameter Token

Simple but less secure

// IF node - validate token
{{$json.query.token}} equals "your-secret-token"

2. Header-Based Auth

Better security

// IF node - check header
{{$json.headers['x-api-key']}} equals "your-api-key"

3. Signature Verification

Best security (for webhooks from services like Stripe, GitHub)

// Code node
const crypto = require('crypto');
const signature = $input.item.headers['x-signature'];
const secret = $credentials.webhookSecret;

const calculatedSig = crypto
  .createHmac('sha256', secret)
  .update(JSON.stringify($input.item.body))
  .digest('hex');

if (signature !== `sha256=${calculatedSig}`) {
  throw new Error('Invalid signature');
}

return $input.item.body;

4. IP Whitelist

Restrict access by IP (n8n workflow settings)

  • Configure in workflow settings
  • Only allow specific IP ranges
  • Use for internal systems

Response Modes

onReceived (Default)

Behavior: Immediate 200 OK response, workflow continues in background

Use when:

  • Long-running workflows
  • Response doesn't depend on workflow result
  • Fire-and-forget processing

Configuration:

{
  responseMode: "onReceived",
  responseCode: 200
}

lastNode (Custom Response)

Behavior: Wait for workflow completion, send custom response

Use when:

  • Need to return data to caller
  • Synchronous processing required
  • Form submissions with confirmation

Configuration:

{
  responseMode: "lastNode"
}

Then add Webhook Response node:

{
  statusCode: 200,
  headers: {
    "Content-Type": "application/json"
  },
  body: {
    "id": "={{$json.record_id}}",
    "status": "success"
  }
}

Error Handling

Pattern 1: Try-Catch with Error Trigger

Main Flow:
  Webhook → [nodes...] → Success Response

Error Flow:
  Error Trigger → Log Error → Slack Alert → Error Response

Error Trigger Configuration:

{
  workflowId: "current-workflow-id"
}

Error Response (if responseMode: "lastNode"):

{
  statusCode: 500,
  body: {
    "status": "error",
    "message": "Processing failed"
  }
}

Pattern 2: Validation Early Exit

Webhook → IF (validate) → [True: Process]
                       └→ [False: Error Response]

False Branch Response:

{
  statusCode: 400,
  body: {
    "status": "error",
    "message": "Invalid data: missing email"
  }
}

Pattern 3: Continue On Fail

Per-node setting: Continue even if node fails

Use case: Non-critical notifications

Webhook → Database (critical) → Slack (continueOnFail: true)

Testing Webhooks

1. Use Manual Trigger

Replace Webhook with Manual Trigger for testing:

Manual Trigger → [set test data] → rest of workflow

2. Use curl

curl -X POST https://n8n.example.com/webhook/form-submit \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "name": "Test User"}'

3. Use Postman/Insomnia

  • Create request collection
  • Test different payloads
  • Verify responses

4. Webhook.site

  • Use webhook.site for testing
  • Copy webhook.site URL to your service
  • View requests and debug

Performance Considerations

Large Payloads

  • Webhook timeout: 120 seconds (default)
  • For large data, consider async processing:
    Webhook → Queue (Redis/DB) → Response (immediate)
    
    Separate Workflow:
    Schedule → Check Queue → Process
    

High Volume

  • Use "Execute Once" mode if processing all items together
  • Consider rate limiting
  • Monitor execution times
  • Scale n8n instance if needed

Retries

  • Webhook calls typically don't retry automatically
  • Implement retry logic on caller side
  • Or use queue pattern for guaranteed processing

Common Gotchas

1. Wrong: Accessing webhook data

{{$json.email}}  // Empty or undefined

Correct

{{$json.body.email}}  // Data is under .body

2. Wrong: Response mode confusion

Using Webhook Response node with responseMode: "onReceived" (ignored)

Correct

Set responseMode: "lastNode" to use Webhook Response node

3. Wrong: No validation

Assuming data is always present and valid

Correct

Validate data early with IF node or Code node

4. Wrong: Hardcoded paths

Using same path for dev/prod

Correct

Use environment variables: {{$env.WEBHOOK_PATH_PREFIX}}/form-submit


Real Template Examples

From n8n template library (1,085 webhook templates):

Simple Form to Slack:

Webhook → Set → Slack

Payment Processing:

Webhook → Verify Signature → Update Database → Send Receipt → Notify Admin

Chat Bot:

Webhook → Parse Command → AI Agent → Format Response → Webhook Response

Use search_templates({query: "webhook"}) to find more!


Checklist for Webhook Workflows

Setup

  • Choose descriptive webhook path
  • Configure HTTP method (POST most common)
  • Choose response mode (onReceived vs lastNode)
  • Test webhook URL before connecting services

Security

  • Add authentication (token, signature, IP whitelist)
  • Validate incoming data
  • Sanitize user input (if storing/displaying)
  • Use HTTPS (always)

Data Handling

  • Remember data is under $json.body
  • Handle missing fields gracefully
  • Transform data to desired format
  • Log important data (for debugging)

Error Handling

  • Add Error Trigger workflow
  • Validate required fields
  • Return appropriate error responses
  • Alert team on failures

Testing

  • Test with curl/Postman
  • Test error scenarios
  • Verify response format
  • Monitor first executions

Summary

Key Points:

  1. Data under $json.body (most common mistake!)
  2. Validate early to catch bad data
  3. Choose response mode based on use case
  4. Secure webhooks with auth
  5. Handle errors gracefully

Pattern: Webhook → Validate → Transform → Action → Response

Related: