All skills
czlonkowski avatar

/n8n-workflow-patterns

@470e9aa

Proven workflow architectural patterns from real n8n workflows. Use when building new workflows, designing workflow structure, choosing workflow patterns, planning workflow architecture, or asking about webhook processing, HTTP API integration, database operations, AI agent workflows, batch processing, or scheduled tasks. Always consult this skill when the user asks to create, build, or design an n8n workflow, automate a process, or connect services — even if they don't explicitly mention 'patterns'. Covers webhook, API, database, AI, batch processing, and scheduled automation architectures. Also use when optimizing a slow workflow or speeding up large-item-count processing (node count, batchSize, all-items vs per-item).

Use this Skill: https://skilld.dev/gh/czlonkowski/n8n-skills/n8n-workflow-patterns

This session only. Nothing lands on disk.

webhook_processing.md

≈2.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

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}}

2. Validation (Optional but Recommended)

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:

Source: SKILL.md on GitHub

2 warnings15d5 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    This skill provides a comprehensive set of architectural patterns and best practices for building n8n workflows. It emphasizes secure design principles, such as using parameterized queries to prevent SQL injection and implementing guardrails to mitigate indirect prompt injection. No malicious behavior or security vulnerabilities were identified.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    5/7 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 470e9aa. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 weeks ago
  • Database
  • n8n
  • workflow
  • automation
  • patterns
  • webhook
  • api-integration
  • scheduling
  • batch-processing
  • ai-agents

README badge

README badge for czlonkowski/n8n-skills/n8n-workflow-patterns

Provides architectural patterns for building n8n workflows across six core scenarios: webhook processing, HTTP API integration, database operations, AI agent workflows, scheduled tasks, and batch processing. Includes pattern selection guides, common components, data flow diagrams, and integration-specific gotchas for Google Sheets and Drive.

Generated from the current SKILL.md.

Which pattern should I use for receiving HTTP requests from external systems?
Use Webhook Processing. It handles receiving HTTP requests, validating data, transforming it, and responding or notifying downstream systems. Common use cases are Stripe webhooks, form submissions, and GitHub integrations.
How do I process large datasets that exceed API batch limits?
Use Batch Processing with SplitInBatches node. Split your dataset into chunks, process each batch in a loop, and accumulate results using $getWorkflowStaticData('global') in a Code node to preserve data across iterations.
What's the correct way to wire SplitInBatches outputs?
SplitInBatches has two outputs: main[1] fires per batch (the loop body), and main[0] fires once after all batches complete. Always add a Limit 1 node after main[0] to prevent duplicate processing.
How do I access webhook payload data in expressions?
Webhook data is nested under $json.body. Use {{$json.body.fieldname}} to access fields, not {{$json.fieldname}}.
When should I use scheduled tasks versus webhooks?
Use Scheduled Tasks for recurring automation like daily reports or periodic data fetching. Use Webhook Processing when you need instant responses to external events.

Generated from the current SKILL.md. These answers refresh after source changes.