diff --git a/docs/skills/SKILLS-INDEX.md b/docs/skills/SKILLS-INDEX.md
index d233b92..8e4d8b0 100644
--- a/docs/skills/SKILLS-INDEX.md
+++ b/docs/skills/SKILLS-INDEX.md
@@ -264,6 +264,30 @@
---
+### n8n-automation (collection)
+**Location:** `docs/skills/n8n-automation/`
+**Source:** skill.fish
+**Triggers:** n8n, workflow automation, expressions, Code node, JavaScript, node configuration
+
+**Purpose:** n8n workflow automation guidance
+
+**What It Covers:**
+- **expression-syntax/** — {{}} expressions, $json, $node, $now variables
+- **javascript-code/** — Code nodes, $input, $helpers, DateTime
+- **node-configuration/** — Operation-aware node setup, property dependencies
+
+**Firefrost Context:**
+- n8n runs on Command Center (63.143.34.217)
+- Used for: Subscription sync, server monitoring, notifications
+
+**Read This When:**
+- Writing n8n expressions
+- Creating Code nodes with JavaScript
+- Configuring complex nodes
+- Troubleshooting n8n errors
+
+---
+
### postgres-patterns
**Location:** `docs/skills/postgres-patterns/SKILL.md`
**Source:** skill.fish (affaan-m/everything-claude-code)
@@ -454,6 +478,22 @@ docs/skills/
│ └── SKILL.md
├── minecraft-mod-dev/
│ └── SKILL.md
+├── n8n-automation/
+│ ├── README.md
+│ ├── expression-syntax/
+│ │ ├── SKILL.md
+│ │ ├── EXAMPLES.md
+│ │ └── COMMON_MISTAKES.md
+│ ├── javascript-code/
+│ │ ├── SKILL.md
+│ │ ├── DATA_ACCESS.md
+│ │ ├── BUILTIN_FUNCTIONS.md
+│ │ ├── COMMON_PATTERNS.md
+│ │ └── ERROR_PATTERNS.md
+│ └── node-configuration/
+│ ├── SKILL.md
+│ ├── OPERATION_PATTERNS.md
+│ └── DEPENDENCIES.md
├── postgres-patterns/
│ └── SKILL.md
├── social-automation/
diff --git a/docs/skills/n8n-automation/README.md b/docs/skills/n8n-automation/README.md
new file mode 100644
index 0000000..7575b64
--- /dev/null
+++ b/docs/skills/n8n-automation/README.md
@@ -0,0 +1,106 @@
+# n8n Automation Skills
+
+Comprehensive n8n workflow automation guidance for Firefrost.
+
+**n8n Location:** Command Center (63.143.34.217)
+**Access:** https://n8n.firefrostgaming.com (or via Cloudflare tunnel)
+
+---
+
+## Available Skills
+
+| Skill | Folder | Purpose |
+|-------|--------|---------|
+| Expression Syntax | `expression-syntax/` | {{}} expressions, $json, $node variables |
+| JavaScript Code | `javascript-code/` | Code nodes, $input, $helpers |
+| Node Configuration | `node-configuration/` | Operation-aware node setup |
+
+---
+
+## Quick Reference
+
+### Expression Syntax (expression-syntax/)
+
+```javascript
+// Access current node data
+{{$json.fieldName}}
+{{$json.body.email}} // Webhook data is under .body!
+
+// Access other nodes
+{{$node["HTTP Request"].json.data}}
+
+// Dates
+{{$now.toFormat('yyyy-MM-dd')}}
+```
+
+**Files:**
+- `SKILL.md` — Core syntax guide
+- `EXAMPLES.md` — Common expression examples
+- `COMMON_MISTAKES.md` — Pitfalls to avoid
+
+---
+
+### JavaScript Code (javascript-code/)
+
+```javascript
+// Basic Code node template
+const items = $input.all();
+
+const processed = items.map(item => ({
+ json: {
+ ...item.json,
+ processed: true
+ }
+}));
+
+return processed; // MUST return [{json: {...}}] format
+```
+
+**Files:**
+- `SKILL.md` — Core guidance
+- `DATA_ACCESS.md` — $input, $json patterns
+- `BUILTIN_FUNCTIONS.md` — $helpers.httpRequest(), DateTime
+- `COMMON_PATTERNS.md` — Reusable code snippets
+- `ERROR_PATTERNS.md` — Debugging help
+
+---
+
+### Node Configuration (node-configuration/)
+
+**Philosophy:** Progressive disclosure — start minimal, add complexity
+
+1. **get_node_essentials** — Use first (91.7% success rate)
+2. **get_property_dependencies** — For complex nodes
+3. **get_node_info** — Full schema when needed
+
+**Files:**
+- `SKILL.md` — Core concepts
+- `OPERATION_PATTERNS.md` — Node-specific patterns
+- `DEPENDENCIES.md` — Property dependency chains
+
+---
+
+## Firefrost n8n Use Cases
+
+| Workflow | Description |
+|----------|-------------|
+| Subscription sync | Stripe → Discord role assignment |
+| Server monitoring | Pterodactyl → Discord alerts |
+| Backup notifications | Server backup status → Discord |
+| Welcome automation | New subscriber → Welcome email/DM |
+
+---
+
+## Common Pitfalls
+
+1. **Webhook data location:** Data is in `$json.body`, not `$json` directly
+2. **Return format:** Code nodes MUST return `[{json: {...}}]`
+3. **Node names:** Case-sensitive, must match exactly
+4. **Expression braces:** Use `{{}}` not `{}`
+
+---
+
+**Source:** skill.fish
+**Added:** 2026-04-09 by Chronicler #73
+
+**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️
diff --git a/docs/skills/n8n-automation/expression-syntax/COMMON_MISTAKES.md b/docs/skills/n8n-automation/expression-syntax/COMMON_MISTAKES.md
new file mode 100644
index 0000000..dba879b
--- /dev/null
+++ b/docs/skills/n8n-automation/expression-syntax/COMMON_MISTAKES.md
@@ -0,0 +1,393 @@
+# Common n8n Expression Mistakes
+
+Complete catalog of expression errors with explanations and fixes.
+
+---
+
+## 1. Missing Curly Braces
+
+**Problem**: Expression not recognized, shows as literal text
+
+❌ **Wrong**:
+```
+$json.email
+```
+
+✅ **Correct**:
+```
+{{$json.email}}
+```
+
+**Why it fails**: n8n treats text without {{ }} as a literal string. Expressions must be wrapped to be evaluated.
+
+**How to identify**: Field shows exact text like "$json.email" instead of actual value.
+
+---
+
+## 2. Webhook Body Access
+
+**Problem**: Undefined values when accessing webhook data
+
+❌ **Wrong**:
+```
+{{$json.name}}
+{{$json.email}}
+{{$json.message}}
+```
+
+✅ **Correct**:
+```
+{{$json.body.name}}
+{{$json.body.email}}
+{{$json.body.message}}
+```
+
+**Why it fails**: Webhook node wraps incoming data under `.body` property. The root `$json` contains headers, params, query, and body.
+
+**Webhook structure**:
+```javascript
+{
+ "headers": {...},
+ "params": {...},
+ "query": {...},
+ "body": { // User data is HERE!
+ "name": "John",
+ "email": "john@example.com"
+ }
+}
+```
+
+**How to identify**: Webhook workflow shows "undefined" for fields that are definitely being sent.
+
+---
+
+## 3. Spaces in Field Names
+
+**Problem**: Syntax error or undefined value
+
+❌ **Wrong**:
+```
+{{$json.first name}}
+{{$json.user data.email}}
+```
+
+✅ **Correct**:
+```
+{{$json['first name']}}
+{{$json['user data'].email}}
+```
+
+**Why it fails**: Spaces break dot notation. JavaScript interprets space as end of property name.
+
+**How to identify**: Error message about unexpected token, or undefined when field exists.
+
+---
+
+## 4. Spaces in Node Names
+
+**Problem**: Cannot access other node's data
+
+❌ **Wrong**:
+```
+{{$node.HTTP Request.json.data}}
+{{$node.Respond to Webhook.json}}
+```
+
+✅ **Correct**:
+```
+{{$node["HTTP Request"].json.data}}
+{{$node["Respond to Webhook"].json}}
+```
+
+**Why it fails**: Node names are treated as object property names and need quotes when they contain spaces.
+
+**How to identify**: Error like "Cannot read property 'Request' of undefined"
+
+---
+
+## 5. Incorrect Node Reference Case
+
+**Problem**: Undefined or wrong data returned
+
+❌ **Wrong**:
+```
+{{$node["http request"].json.data}} // lowercase
+{{$node["Http Request"].json.data}} // wrong capitalization
+```
+
+✅ **Correct**:
+```
+{{$node["HTTP Request"].json.data}} // exact match
+```
+
+**Why it fails**: Node names are **case-sensitive**. Must match exactly as shown in workflow.
+
+**How to identify**: Undefined value even though node exists and has data.
+
+---
+
+## 6. Double Wrapping
+
+**Problem**: Literal {{ }} appears in output
+
+❌ **Wrong**:
+```
+{{{$json.field}}}
+```
+
+✅ **Correct**:
+```
+{{$json.field}}
+```
+
+**Why it fails**: Only one set of {{ }} is needed. Extra braces are treated as literal characters.
+
+**How to identify**: Output shows "{{value}}" instead of just "value".
+
+---
+
+## 7. Array Access with Dots
+
+**Problem**: Syntax error or undefined
+
+❌ **Wrong**:
+```
+{{$json.items.0.name}}
+{{$json.users.1.email}}
+```
+
+✅ **Correct**:
+```
+{{$json.items[0].name}}
+{{$json.users[1].email}}
+```
+
+**Why it fails**: Array indices require brackets, not dots. Number after dot is invalid JavaScript.
+
+**How to identify**: Syntax error or "Cannot read property '0' of undefined"
+
+---
+
+## 8. Using Expressions in Code Nodes
+
+**Problem**: Literal string instead of value, or errors
+
+❌ **Wrong (in Code node)**:
+```javascript
+const email = '{{$json.email}}';
+const name = '={{$json.body.name}}';
+```
+
+✅ **Correct (in Code node)**:
+```javascript
+const email = $json.email;
+const name = $json.body.name;
+
+// Or using Code node API
+const email = $input.item.json.email;
+const allItems = $input.all();
+```
+
+**Why it fails**: Code nodes have **direct access** to data. The {{ }} syntax is for expression fields in other nodes, not for JavaScript code.
+
+**How to identify**: Literal string "{{$json.email}}" appears in Code node output instead of actual value.
+
+---
+
+## 9. Missing Quotes in $node Reference
+
+**Problem**: Syntax error
+
+❌ **Wrong**:
+```
+{{$node[HTTP Request].json.data}}
+```
+
+✅ **Correct**:
+```
+{{$node["HTTP Request"].json.data}}
+```
+
+**Why it fails**: Node names must be quoted strings inside brackets.
+
+**How to identify**: Syntax error "Unexpected identifier"
+
+---
+
+## 10. Incorrect Property Path
+
+**Problem**: Undefined value
+
+❌ **Wrong**:
+```
+{{$json.data.items.name}} // items is an array
+{{$json.user.email}} // user doesn't exist, it's userData
+```
+
+✅ **Correct**:
+```
+{{$json.data.items[0].name}} // access array element
+{{$json.userData.email}} // correct property name
+```
+
+**Why it fails**: Wrong path to data. Arrays need index, property names must be exact.
+
+**How to identify**: Check actual data structure using expression editor preview.
+
+---
+
+## 11. Using = Prefix Outside JSON
+
+**Problem**: Literal "=" appears in output
+
+❌ **Wrong (in text field)**:
+```
+Email: ={{$json.email}}
+```
+
+✅ **Correct (in text field)**:
+```
+Email: {{$json.email}}
+```
+
+**Note**: The `=` prefix is **only** needed in JSON mode or when you want to set entire field value to expression result:
+
+```javascript
+// JSON mode (set property to expression)
+{
+ "email": "={{$json.body.email}}"
+}
+
+// Text mode (no = needed)
+Hello {{$json.body.name}}!
+```
+
+**Why it fails**: The `=` is parsed as literal text in non-JSON contexts.
+
+**How to identify**: Output shows "=john@example.com" instead of "john@example.com"
+
+---
+
+## 12. Expressions in Webhook Path
+
+**Problem**: Path doesn't update, validation error
+
+❌ **Wrong**:
+```
+path: "{{$json.user_id}}/webhook"
+path: "users/={{$env.TENANT_ID}}"
+```
+
+✅ **Correct**:
+```
+path: "my-webhook" // Static paths only
+path: "user-webhook/:userId" // Use dynamic URL parameters instead
+```
+
+**Why it fails**: Webhook paths must be static. Use dynamic URL parameters (`:paramName`) instead of expressions.
+
+**How to identify**: Webhook path doesn't change or validation warns about invalid path.
+
+---
+
+## 13. Forgetting .json in $node Reference
+
+**Problem**: Undefined or wrong data
+
+❌ **Wrong**:
+```
+{{$node["HTTP Request"].data}} // Missing .json
+{{$node["Webhook"].body.email}} // Missing .json
+```
+
+✅ **Correct**:
+```
+{{$node["HTTP Request"].json.data}}
+{{$node["Webhook"].json.body.email}}
+```
+
+**Why it fails**: Node data is always under `.json` property (or `.binary` for binary data).
+
+**How to identify**: Undefined value when you know the node has data.
+
+---
+
+## 14. String Concatenation Confusion
+
+**Problem**: Attempting JavaScript template literals
+
+❌ **Wrong**:
+```
+`Hello ${$json.name}!` // Template literal syntax
+"Hello " + $json.name + "!" // String concatenation
+```
+
+✅ **Correct**:
+```
+Hello {{$json.name}}! // n8n expressions auto-concatenate
+```
+
+**Why it fails**: n8n expressions don't use JavaScript template literal syntax. Adjacent text and expressions are automatically concatenated.
+
+**How to identify**: Literal backticks or + symbols appear in output.
+
+---
+
+## 15. Empty Expression Brackets
+
+**Problem**: Literal {{}} in output
+
+❌ **Wrong**:
+```
+{{}}
+{{ }}
+```
+
+✅ **Correct**:
+```
+{{$json.field}} // Include expression content
+```
+
+**Why it fails**: Empty expression brackets have nothing to evaluate.
+
+**How to identify**: Literal "{{ }}" text appears in output.
+
+---
+
+## Quick Reference Table
+
+| Error | Symptom | Fix |
+|-------|---------|-----|
+| No {{ }} | Literal text | Add {{ }} |
+| Webhook data | Undefined | Add `.body` |
+| Space in field | Syntax error | Use `['field name']` |
+| Space in node | Undefined | Use `["Node Name"]` |
+| Wrong case | Undefined | Match exact case |
+| Double {{ }} | Literal braces | Remove extra {{ }} |
+| .0 array | Syntax error | Use [0] |
+| {{ }} in Code | Literal string | Remove {{ }} |
+| No quotes in $node | Syntax error | Add quotes |
+| Wrong path | Undefined | Check data structure |
+| = in text | Literal = | Remove = prefix |
+| Dynamic path | Doesn't work | Use static path |
+| Missing .json | Undefined | Add .json |
+| Template literals | Literal text | Use {{ }} |
+| Empty {{ }} | Literal braces | Add expression |
+
+---
+
+## Debugging Process
+
+When expression doesn't work:
+
+1. **Check braces**: Is it wrapped in {{ }}?
+2. **Check data source**: Is it webhook data? Add `.body`
+3. **Check spaces**: Field or node name has spaces? Use brackets
+4. **Check case**: Does node name match exactly?
+5. **Check path**: Is the property path correct?
+6. **Use expression editor**: Preview shows actual result
+7. **Check context**: Is it a Code node? Remove {{ }}
+
+---
+
+**Related**: See [EXAMPLES.md](EXAMPLES.md) for working examples of correct syntax.
diff --git a/docs/skills/n8n-automation/expression-syntax/EXAMPLES.md b/docs/skills/n8n-automation/expression-syntax/EXAMPLES.md
new file mode 100644
index 0000000..35a39c7
--- /dev/null
+++ b/docs/skills/n8n-automation/expression-syntax/EXAMPLES.md
@@ -0,0 +1,483 @@
+# n8n Expression Examples
+
+Real working examples from n8n workflows.
+
+---
+
+## Example 1: Webhook Form Submission
+
+**Scenario**: Form submission webhook posts to Slack
+
+**Workflow**: Webhook → Slack
+
+**Webhook Input** (POST):
+```json
+{
+ "name": "John Doe",
+ "email": "john@example.com",
+ "company": "Acme Corp",
+ "message": "Interested in your product"
+}
+```
+
+**Webhook Node Output**:
+```json
+{
+ "headers": {"content-type": "application/json"},
+ "params": {},
+ "query": {},
+ "body": {
+ "name": "John Doe",
+ "email": "john@example.com",
+ "company": "Acme Corp",
+ "message": "Interested in your product"
+ }
+}
+```
+
+**In Slack Node** (text field):
+```
+New form submission! 📝
+
+Name: {{$json.body.name}}
+Email: {{$json.body.email}}
+Company: {{$json.body.company}}
+Message: {{$json.body.message}}
+```
+
+**Output**:
+```
+New form submission! 📝
+
+Name: John Doe
+Email: john@example.com
+Company: Acme Corp
+Message: Interested in your product
+```
+
+---
+
+## Example 2: HTTP API to Database
+
+**Scenario**: Fetch user data from API and insert into database
+
+**Workflow**: Schedule → HTTP Request → Postgres
+
+**HTTP Request Returns**:
+```json
+{
+ "data": {
+ "users": [
+ {
+ "id": 123,
+ "name": "Alice Smith",
+ "email": "alice@example.com",
+ "role": "admin"
+ }
+ ]
+ }
+}
+```
+
+**In Postgres Node** (INSERT statement):
+```sql
+INSERT INTO users (user_id, name, email, role, synced_at)
+VALUES (
+ {{$json.data.users[0].id}},
+ '{{$json.data.users[0].name}}',
+ '{{$json.data.users[0].email}}',
+ '{{$json.data.users[0].role}}',
+ '{{$now.toFormat('yyyy-MM-dd HH:mm:ss')}}'
+)
+```
+
+**Result**: User inserted with current timestamp
+
+---
+
+## Example 3: Multi-Node Data Flow
+
+**Scenario**: Webhook → HTTP Request → Email
+
+**Workflow Structure**:
+1. Webhook receives order ID
+2. HTTP Request fetches order details
+3. Email sends confirmation
+
+### Node 1: Webhook
+
+**Receives**:
+```json
+{
+ "body": {
+ "order_id": "ORD-12345"
+ }
+}
+```
+
+### Node 2: HTTP Request
+
+**URL field**:
+```
+https://api.example.com/orders/{{$json.body.order_id}}
+```
+
+**Returns**:
+```json
+{
+ "order": {
+ "id": "ORD-12345",
+ "customer": "Bob Jones",
+ "total": 99.99,
+ "items": ["Widget", "Gadget"]
+ }
+}
+```
+
+### Node 3: Email
+
+**Subject**:
+```
+Order {{$node["Webhook"].json.body.order_id}} Confirmed
+```
+
+**Body**:
+```
+Dear {{$node["HTTP Request"].json.order.customer}},
+
+Your order {{$node["Webhook"].json.body.order_id}} has been confirmed!
+
+Total: ${{$node["HTTP Request"].json.order.total}}
+Items: {{$node["HTTP Request"].json.order.items.join(', ')}}
+
+Thank you for your purchase!
+```
+
+**Email Result**:
+```
+Subject: Order ORD-12345 Confirmed
+
+Dear Bob Jones,
+
+Your order ORD-12345 has been confirmed!
+
+Total: $99.99
+Items: Widget, Gadget
+
+Thank you for your purchase!
+```
+
+---
+
+## Example 4: Date Formatting
+
+**Scenario**: Various date format outputs
+
+**Current Time**: 2025-10-20 14:30:45
+
+### ISO Format
+```javascript
+{{$now.toISO()}}
+```
+**Output**: `2025-10-20T14:30:45.000Z`
+
+### Custom Date Format
+```javascript
+{{$now.toFormat('yyyy-MM-dd')}}
+```
+**Output**: `2025-10-20`
+
+### Time Only
+```javascript
+{{$now.toFormat('HH:mm:ss')}}
+```
+**Output**: `14:30:45`
+
+### Full Readable Format
+```javascript
+{{$now.toFormat('MMMM dd, yyyy')}}
+```
+**Output**: `October 20, 2025`
+
+### Date Math - Future
+```javascript
+{{$now.plus({days: 7}).toFormat('yyyy-MM-dd')}}
+```
+**Output**: `2025-10-27`
+
+### Date Math - Past
+```javascript
+{{$now.minus({hours: 24}).toFormat('yyyy-MM-dd HH:mm')}}
+```
+**Output**: `2025-10-19 14:30`
+
+---
+
+## Example 5: Array Operations
+
+**Data**:
+```json
+{
+ "users": [
+ {"name": "Alice", "email": "alice@example.com"},
+ {"name": "Bob", "email": "bob@example.com"},
+ {"name": "Charlie", "email": "charlie@example.com"}
+ ]
+}
+```
+
+### First User
+```javascript
+{{$json.users[0].name}}
+```
+**Output**: `Alice`
+
+### Last User
+```javascript
+{{$json.users[$json.users.length - 1].name}}
+```
+**Output**: `Charlie`
+
+### All Emails (Join)
+```javascript
+{{$json.users.map(u => u.email).join(', ')}}
+```
+**Output**: `alice@example.com, bob@example.com, charlie@example.com`
+
+### Array Length
+```javascript
+{{$json.users.length}}
+```
+**Output**: `3`
+
+---
+
+## Example 6: Conditional Logic
+
+**Data**:
+```json
+{
+ "order": {
+ "status": "completed",
+ "total": 150
+ }
+}
+```
+
+### Ternary Operator
+```javascript
+{{$json.order.status === 'completed' ? 'Order Complete ✓' : 'Pending...'}}
+```
+**Output**: `Order Complete ✓`
+
+### Default Values
+```javascript
+{{$json.order.notes || 'No notes provided'}}
+```
+**Output**: `No notes provided` (if notes field doesn't exist)
+
+### Multiple Conditions
+```javascript
+{{$json.order.total > 100 ? 'Premium Customer' : 'Standard Customer'}}
+```
+**Output**: `Premium Customer`
+
+---
+
+## Example 7: String Manipulation
+
+**Data**:
+```json
+{
+ "user": {
+ "email": "JOHN@EXAMPLE.COM",
+ "message": " Hello World "
+ }
+}
+```
+
+### Lowercase
+```javascript
+{{$json.user.email.toLowerCase()}}
+```
+**Output**: `john@example.com`
+
+### Uppercase
+```javascript
+{{$json.user.message.toUpperCase()}}
+```
+**Output**: ` HELLO WORLD `
+
+### Trim
+```javascript
+{{$json.user.message.trim()}}
+```
+**Output**: `Hello World`
+
+### Substring
+```javascript
+{{$json.user.email.substring(0, 4)}}
+```
+**Output**: `JOHN`
+
+### Replace
+```javascript
+{{$json.user.message.replace('World', 'n8n')}}
+```
+**Output**: ` Hello n8n `
+
+---
+
+## Example 8: Fields with Spaces
+
+**Data**:
+```json
+{
+ "user data": {
+ "first name": "Jane",
+ "last name": "Doe",
+ "phone number": "+1234567890"
+ }
+}
+```
+
+### Bracket Notation
+```javascript
+{{$json['user data']['first name']}}
+```
+**Output**: `Jane`
+
+### Combined
+```javascript
+{{$json['user data']['first name']}} {{$json['user data']['last name']}}
+```
+**Output**: `Jane Doe`
+
+### Nested Spaces
+```javascript
+Contact: {{$json['user data']['phone number']}}
+```
+**Output**: `Contact: +1234567890`
+
+---
+
+## Example 9: Code Node (Direct Access)
+
+**Code Node**: Transform webhook data
+
+**Input** (from Webhook node):
+```json
+{
+ "body": {
+ "items": ["apple", "banana", "cherry"]
+ }
+}
+```
+
+**Code** (JavaScript):
+```javascript
+// ✅ Direct access (no {{ }})
+const items = $json.body.items;
+
+// Transform to uppercase
+const uppercased = items.map(item => item.toUpperCase());
+
+// Return in n8n format
+return [{
+ json: {
+ original: items,
+ transformed: uppercased,
+ count: items.length
+ }
+}];
+```
+
+**Output**:
+```json
+{
+ "original": ["apple", "banana", "cherry"],
+ "transformed": ["APPLE", "BANANA", "CHERRY"],
+ "count": 3
+}
+```
+
+---
+
+## Example 10: Environment Variables
+
+**Setup**: Environment variable `API_KEY=secret123`
+
+### In HTTP Request (Headers)
+```javascript
+Authorization: Bearer {{$env.API_KEY}}
+```
+**Result**: `Authorization: Bearer secret123`
+
+### In URL
+```javascript
+https://api.example.com/data?key={{$env.API_KEY}}
+```
+**Result**: `https://api.example.com/data?key=secret123`
+
+---
+
+## Template from Real Workflow
+
+**Based on n8n template #2947** (Weather to Slack)
+
+### Workflow Structure
+Webhook → OpenStreetMap API → Weather API → Slack
+
+### Webhook Slash Command
+**Input**: `/weather London`
+
+**Webhook receives**:
+```json
+{
+ "body": {
+ "text": "London"
+ }
+}
+```
+
+### OpenStreetMap API
+**URL**:
+```
+https://nominatim.openstreetmap.org/search?q={{$json.body.text}}&format=json
+```
+
+### Weather API (NWS)
+**URL**:
+```
+https://api.weather.gov/points/{{$node["OpenStreetMap"].json[0].lat}},{{$node["OpenStreetMap"].json[0].lon}}
+```
+
+### Slack Message
+```
+Weather for {{$json.body.text}}:
+
+Temperature: {{$node["Weather API"].json.properties.temperature.value}}°C
+Conditions: {{$node["Weather API"].json.properties.shortForecast}}
+```
+
+---
+
+## Summary
+
+**Key Patterns**:
+1. Webhook data is under `.body`
+2. Use `{{}}` for expressions (except Code nodes)
+3. Reference other nodes with `$node["Node Name"].json`
+4. Use brackets for field names with spaces
+5. Node names are case-sensitive
+
+**Most Common Uses**:
+- `{{$json.body.field}}` - Webhook data
+- `{{$node["Name"].json.field}}` - Other node data
+- `{{$now.toFormat('yyyy-MM-dd')}}` - Timestamps
+- `{{$json.array[0].field}}` - Array access
+- `{{$json.field || 'default'}}` - Default values
+
+---
+
+**Related**: See [COMMON_MISTAKES.md](COMMON_MISTAKES.md) for error examples and fixes.
diff --git a/docs/skills/n8n-automation/expression-syntax/README.md b/docs/skills/n8n-automation/expression-syntax/README.md
new file mode 100644
index 0000000..0fe0ded
--- /dev/null
+++ b/docs/skills/n8n-automation/expression-syntax/README.md
@@ -0,0 +1,93 @@
+# n8n Expression Syntax
+
+Expert guide for writing correct n8n expressions in workflows.
+
+---
+
+## Purpose
+
+Teaches correct n8n expression syntax ({{ }} patterns) and fixes common mistakes, especially the critical webhook data structure gotcha.
+
+## Activates On
+
+- expression
+- {{}} syntax
+- $json, $node, $now, $env
+- webhook data
+- troubleshoot expression error
+- undefined in workflow
+
+## File Count
+
+4 files, ~450 lines total
+
+## Dependencies
+
+**n8n-mcp tools**:
+- None directly (syntax knowledge skill)
+- Works with n8n-mcp validation tools
+
+**Related skills**:
+- n8n Workflow Patterns (uses expressions in examples)
+- n8n MCP Tools Expert (validates expressions)
+- n8n Node Configuration (when expressions are needed)
+
+## Coverage
+
+### Core Topics
+- Expression format ({{ }})
+- Core variables ($json, $node, $now, $env)
+- **Webhook data structure** ($json.body.*)
+- When NOT to use expressions (Code nodes)
+
+### Common Patterns
+- Accessing nested fields
+- Referencing other nodes
+- Array and object access
+- Date/time formatting
+- String manipulation
+
+### Error Prevention
+- 15 common mistakes with fixes
+- Quick reference table
+- Debugging process
+
+## Evaluations
+
+4 scenarios (100% coverage expected):
+1. **eval-001**: Missing curly braces
+2. **eval-002**: Webhook body data access (critical!)
+3. **eval-003**: Code node vs expression confusion
+4. **eval-004**: Node reference syntax
+
+## Key Features
+
+✅ **Critical Gotcha Highlighted**: Webhook data under `.body`
+✅ **Real Examples**: From MCP testing and real templates
+✅ **Quick Fixes Table**: Fast reference for common errors
+✅ **Code vs Expression**: Clear distinction
+✅ **Comprehensive**: Covers 95% of expression use cases
+
+## Files
+
+- **SKILL.md** (285 lines) - Main content with all essential knowledge
+- **COMMON_MISTAKES.md** (380 lines) - Complete error catalog with 15 common mistakes
+- **EXAMPLES.md** (450 lines) - 10 real working examples
+- **README.md** (this file) - Skill metadata
+
+## Success Metrics
+
+**Expected outcomes**:
+- Users correctly wrap expressions in {{ }}
+- Zero webhook `.body` access errors
+- No expressions used in Code nodes
+- Correct $node reference syntax
+
+## Last Updated
+
+2025-10-20
+
+---
+
+**Part of**: n8n-skills repository
+**Conceived by**: Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
diff --git a/docs/skills/n8n-automation/expression-syntax/SKILL.md b/docs/skills/n8n-automation/expression-syntax/SKILL.md
new file mode 100644
index 0000000..2276cdb
--- /dev/null
+++ b/docs/skills/n8n-automation/expression-syntax/SKILL.md
@@ -0,0 +1,516 @@
+---
+name: n8n-expression-syntax
+description: Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.
+---
+
+# n8n Expression Syntax
+
+Expert guide for writing correct n8n expressions in workflows.
+
+---
+
+## Expression Format
+
+All dynamic content in n8n uses **double curly braces**:
+
+```
+{{expression}}
+```
+
+**Examples**:
+```
+✅ {{$json.email}}
+✅ {{$json.body.name}}
+✅ {{$node["HTTP Request"].json.data}}
+❌ $json.email (no braces - treated as literal text)
+❌ {$json.email} (single braces - invalid)
+```
+
+---
+
+## Core Variables
+
+### $json - Current Node Output
+
+Access data from the current node:
+
+```javascript
+{{$json.fieldName}}
+{{$json['field with spaces']}}
+{{$json.nested.property}}
+{{$json.items[0].name}}
+```
+
+### $node - Reference Other Nodes
+
+Access data from any previous node:
+
+```javascript
+{{$node["Node Name"].json.fieldName}}
+{{$node["HTTP Request"].json.data}}
+{{$node["Webhook"].json.body.email}}
+```
+
+**Important**:
+- Node names **must** be in quotes
+- Node names are **case-sensitive**
+- Must match exact node name from workflow
+
+### $now - Current Timestamp
+
+Access current date/time:
+
+```javascript
+{{$now}}
+{{$now.toFormat('yyyy-MM-dd')}}
+{{$now.toFormat('HH:mm:ss')}}
+{{$now.plus({days: 7})}}
+```
+
+### $env - Environment Variables
+
+Access environment variables:
+
+```javascript
+{{$env.API_KEY}}
+{{$env.DATABASE_URL}}
+```
+
+---
+
+## 🚨 CRITICAL: Webhook Data Structure
+
+**Most Common Mistake**: Webhook data is **NOT** at the root!
+
+### Webhook Node Output Structure
+
+```javascript
+{
+ "headers": {...},
+ "params": {...},
+ "query": {...},
+ "body": { // ⚠️ USER DATA IS HERE!
+ "name": "John",
+ "email": "john@example.com",
+ "message": "Hello"
+ }
+}
+```
+
+### Correct Webhook Data Access
+
+```javascript
+❌ WRONG: {{$json.name}}
+❌ WRONG: {{$json.email}}
+
+✅ CORRECT: {{$json.body.name}}
+✅ CORRECT: {{$json.body.email}}
+✅ CORRECT: {{$json.body.message}}
+```
+
+**Why**: Webhook node wraps incoming data under `.body` property to preserve headers, params, and query parameters.
+
+---
+
+## Common Patterns
+
+### Access Nested Fields
+
+```javascript
+// Simple nesting
+{{$json.user.email}}
+
+// Array access
+{{$json.data[0].name}}
+{{$json.items[0].id}}
+
+// Bracket notation for spaces
+{{$json['field name']}}
+{{$json['user data']['first name']}}
+```
+
+### Reference Other Nodes
+
+```javascript
+// Node without spaces
+{{$node["Set"].json.value}}
+
+// Node with spaces (common!)
+{{$node["HTTP Request"].json.data}}
+{{$node["Respond to Webhook"].json.message}}
+
+// Webhook node
+{{$node["Webhook"].json.body.email}}
+```
+
+### Combine Variables
+
+```javascript
+// Concatenation (automatic)
+Hello {{$json.body.name}}!
+
+// In URLs
+https://api.example.com/users/{{$json.body.user_id}}
+
+// In object properties
+{
+ "name": "={{$json.body.name}}",
+ "email": "={{$json.body.email}}"
+}
+```
+
+---
+
+## When NOT to Use Expressions
+
+### ❌ Code Nodes
+
+Code nodes use **direct JavaScript access**, NOT expressions!
+
+```javascript
+// ❌ WRONG in Code node
+const email = '={{$json.email}}';
+const name = '{{$json.body.name}}';
+
+// ✅ CORRECT in Code node
+const email = $json.email;
+const name = $json.body.name;
+
+// Or using Code node API
+const email = $input.item.json.email;
+const allItems = $input.all();
+```
+
+### ❌ Webhook Paths
+
+```javascript
+// ❌ WRONG
+path: "{{$json.user_id}}/webhook"
+
+// ✅ CORRECT
+path: "user-webhook" // Static paths only
+```
+
+### ❌ Credential Fields
+
+```javascript
+// ❌ WRONG
+apiKey: "={{$env.API_KEY}}"
+
+// ✅ CORRECT
+Use n8n credential system, not expressions
+```
+
+---
+
+## Validation Rules
+
+### 1. Always Use {{}}
+
+Expressions **must** be wrapped in double curly braces.
+
+```javascript
+❌ $json.field
+✅ {{$json.field}}
+```
+
+### 2. Use Quotes for Spaces
+
+Field or node names with spaces require **bracket notation**:
+
+```javascript
+❌ {{$json.field name}}
+✅ {{$json['field name']}}
+
+❌ {{$node.HTTP Request.json}}
+✅ {{$node["HTTP Request"].json}}
+```
+
+### 3. Match Exact Node Names
+
+Node references are **case-sensitive**:
+
+```javascript
+❌ {{$node["http request"].json}} // lowercase
+❌ {{$node["Http Request"].json}} // wrong case
+✅ {{$node["HTTP Request"].json}} // exact match
+```
+
+### 4. No Nested {{}}
+
+Don't double-wrap expressions:
+
+```javascript
+❌ {{{$json.field}}}
+✅ {{$json.field}}
+```
+
+---
+
+## Common Mistakes
+
+For complete error catalog with fixes, see [COMMON_MISTAKES.md](COMMON_MISTAKES.md)
+
+### Quick Fixes
+
+| Mistake | Fix |
+|---------|-----|
+| `$json.field` | `{{$json.field}}` |
+| `{{$json.field name}}` | `{{$json['field name']}}` |
+| `{{$node.HTTP Request}}` | `{{$node["HTTP Request"]}}` |
+| `{{{$json.field}}}` | `{{$json.field}}` |
+| `{{$json.name}}` (webhook) | `{{$json.body.name}}` |
+| `'={{$json.email}}'` (Code node) | `$json.email` |
+
+---
+
+## Working Examples
+
+For real workflow examples, see [EXAMPLES.md](EXAMPLES.md)
+
+### Example 1: Webhook to Slack
+
+**Webhook receives**:
+```json
+{
+ "body": {
+ "name": "John Doe",
+ "email": "john@example.com",
+ "message": "Hello!"
+ }
+}
+```
+
+**In Slack node text field**:
+```
+New form submission!
+
+Name: {{$json.body.name}}
+Email: {{$json.body.email}}
+Message: {{$json.body.message}}
+```
+
+### Example 2: HTTP Request to Email
+
+**HTTP Request returns**:
+```json
+{
+ "data": {
+ "items": [
+ {"name": "Product 1", "price": 29.99}
+ ]
+ }
+}
+```
+
+**In Email node** (reference HTTP Request):
+```
+Product: {{$node["HTTP Request"].json.data.items[0].name}}
+Price: ${{$node["HTTP Request"].json.data.items[0].price}}
+```
+
+### Example 3: Format Timestamp
+
+```javascript
+// Current date
+{{$now.toFormat('yyyy-MM-dd')}}
+// Result: 2025-10-20
+
+// Time
+{{$now.toFormat('HH:mm:ss')}}
+// Result: 14:30:45
+
+// Full datetime
+{{$now.toFormat('yyyy-MM-dd HH:mm')}}
+// Result: 2025-10-20 14:30
+```
+
+---
+
+## Data Type Handling
+
+### Arrays
+
+```javascript
+// First item
+{{$json.users[0].email}}
+
+// Array length
+{{$json.users.length}}
+
+// Last item
+{{$json.users[$json.users.length - 1].name}}
+```
+
+### Objects
+
+```javascript
+// Dot notation (no spaces)
+{{$json.user.email}}
+
+// Bracket notation (with spaces or dynamic)
+{{$json['user data'].email}}
+```
+
+### Strings
+
+```javascript
+// Concatenation (automatic)
+Hello {{$json.name}}!
+
+// String methods
+{{$json.email.toLowerCase()}}
+{{$json.name.toUpperCase()}}
+```
+
+### Numbers
+
+```javascript
+// Direct use
+{{$json.price}}
+
+// Math operations
+{{$json.price * 1.1}} // Add 10%
+{{$json.quantity + 5}}
+```
+
+---
+
+## Advanced Patterns
+
+### Conditional Content
+
+```javascript
+// Ternary operator
+{{$json.status === 'active' ? 'Active User' : 'Inactive User'}}
+
+// Default values
+{{$json.email || 'no-email@example.com'}}
+```
+
+### Date Manipulation
+
+```javascript
+// Add days
+{{$now.plus({days: 7}).toFormat('yyyy-MM-dd')}}
+
+// Subtract hours
+{{$now.minus({hours: 24}).toISO()}}
+
+// Set specific date
+{{DateTime.fromISO('2025-12-25').toFormat('MMMM dd, yyyy')}}
+```
+
+### String Manipulation
+
+```javascript
+// Substring
+{{$json.email.substring(0, 5)}}
+
+// Replace
+{{$json.message.replace('old', 'new')}}
+
+// Split and join
+{{$json.tags.split(',').join(', ')}}
+```
+
+---
+
+## Debugging Expressions
+
+### Test in Expression Editor
+
+1. Click field with expression
+2. Open expression editor (click "fx" icon)
+3. See live preview of result
+4. Check for errors highlighted in red
+
+### Common Error Messages
+
+**"Cannot read property 'X' of undefined"**
+→ Parent object doesn't exist
+→ Check your data path
+
+**"X is not a function"**
+→ Trying to call method on non-function
+→ Check variable type
+
+**Expression shows as literal text**
+→ Missing {{ }}
+→ Add curly braces
+
+---
+
+## Expression Helpers
+
+### Available Methods
+
+**String**:
+- `.toLowerCase()`, `.toUpperCase()`
+- `.trim()`, `.replace()`, `.substring()`
+- `.split()`, `.includes()`
+
+**Array**:
+- `.length`, `.map()`, `.filter()`
+- `.find()`, `.join()`, `.slice()`
+
+**DateTime** (Luxon):
+- `.toFormat()`, `.toISO()`, `.toLocal()`
+- `.plus()`, `.minus()`, `.set()`
+
+**Number**:
+- `.toFixed()`, `.toString()`
+- Math operations: `+`, `-`, `*`, `/`, `%`
+
+---
+
+## Best Practices
+
+### ✅ Do
+
+- Always use {{ }} for dynamic content
+- Use bracket notation for field names with spaces
+- Reference webhook data from `.body`
+- Use $node for data from other nodes
+- Test expressions in expression editor
+
+### ❌ Don't
+
+- Don't use expressions in Code nodes
+- Don't forget quotes around node names with spaces
+- Don't double-wrap with extra {{ }}
+- Don't assume webhook data is at root (it's under .body!)
+- Don't use expressions in webhook paths or credentials
+
+---
+
+## Related Skills
+
+- **n8n MCP Tools Expert**: Learn how to validate expressions using MCP tools
+- **n8n Workflow Patterns**: See expressions in real workflow examples
+- **n8n Node Configuration**: Understand when expressions are needed
+
+---
+
+## Summary
+
+**Essential Rules**:
+1. Wrap expressions in {{ }}
+2. Webhook data is under `.body`
+3. No {{ }} in Code nodes
+4. Quote node names with spaces
+5. Node names are case-sensitive
+
+**Most Common Mistakes**:
+- Missing {{ }} → Add braces
+- `{{$json.name}}` in webhooks → Use `{{$json.body.name}}`
+- `{{$json.email}}` in Code → Use `$json.email`
+- `{{$node.HTTP Request}}` → Use `{{$node["HTTP Request"]}}`
+
+For more details, see:
+- [COMMON_MISTAKES.md](COMMON_MISTAKES.md) - Complete error catalog
+- [EXAMPLES.md](EXAMPLES.md) - Real workflow examples
+
+---
+
+**Need Help?** Reference the n8n expression documentation or use n8n-mcp validation tools to check your expressions.
diff --git a/docs/skills/n8n-automation/javascript-code/BUILTIN_FUNCTIONS.md b/docs/skills/n8n-automation/javascript-code/BUILTIN_FUNCTIONS.md
new file mode 100644
index 0000000..71c32b7
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/BUILTIN_FUNCTIONS.md
@@ -0,0 +1,764 @@
+# Built-in Functions - JavaScript Code Node
+
+Complete reference for n8n's built-in JavaScript functions and helpers.
+
+---
+
+## Overview
+
+n8n Code nodes provide powerful built-in functions beyond standard JavaScript. This guide covers:
+
+1. **$helpers.httpRequest()** - Make HTTP requests
+2. **DateTime (Luxon)** - Advanced date/time operations
+3. **$jmespath()** - Query JSON structures
+4. **$getWorkflowStaticData()** - Persistent storage
+5. **Standard JavaScript Globals** - Math, JSON, console, etc.
+6. **Available Node.js Modules** - crypto, Buffer, URL
+
+---
+
+## 1. $helpers.httpRequest() - HTTP Requests
+
+Make HTTP requests directly from Code nodes without using HTTP Request node.
+
+### Basic Usage
+
+```javascript
+const response = await $helpers.httpRequest({
+ method: 'GET',
+ url: 'https://api.example.com/users'
+});
+
+return [{json: {data: response}}];
+```
+
+### Complete Options
+
+```javascript
+const response = await $helpers.httpRequest({
+ method: 'POST', // GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
+ url: 'https://api.example.com/users',
+ headers: {
+ 'Authorization': 'Bearer token123',
+ 'Content-Type': 'application/json',
+ 'User-Agent': 'n8n-workflow'
+ },
+ body: {
+ name: 'John Doe',
+ email: 'john@example.com'
+ },
+ qs: { // Query string parameters
+ page: 1,
+ limit: 10
+ },
+ timeout: 10000, // Milliseconds (default: no timeout)
+ json: true, // Auto-parse JSON response (default: true)
+ simple: false, // Don't throw on HTTP errors (default: true)
+ resolveWithFullResponse: false // Return only body (default: false)
+});
+```
+
+### GET Request
+
+```javascript
+// Simple GET
+const users = await $helpers.httpRequest({
+ method: 'GET',
+ url: 'https://api.example.com/users'
+});
+
+return [{json: {users}}];
+```
+
+```javascript
+// GET with query parameters
+const results = await $helpers.httpRequest({
+ method: 'GET',
+ url: 'https://api.example.com/search',
+ qs: {
+ q: 'javascript',
+ page: 1,
+ per_page: 50
+ }
+});
+
+return [{json: results}];
+```
+
+### POST Request
+
+```javascript
+// POST with JSON body
+const newUser = await $helpers.httpRequest({
+ method: 'POST',
+ url: 'https://api.example.com/users',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + $env.API_KEY
+ },
+ body: {
+ name: $json.body.name,
+ email: $json.body.email,
+ role: 'user'
+ }
+});
+
+return [{json: newUser}];
+```
+
+### PUT/PATCH Request
+
+```javascript
+// Update resource
+const updated = await $helpers.httpRequest({
+ method: 'PATCH',
+ url: `https://api.example.com/users/${userId}`,
+ body: {
+ name: 'Updated Name',
+ status: 'active'
+ }
+});
+
+return [{json: updated}];
+```
+
+### DELETE Request
+
+```javascript
+// Delete resource
+await $helpers.httpRequest({
+ method: 'DELETE',
+ url: `https://api.example.com/users/${userId}`,
+ headers: {
+ 'Authorization': 'Bearer ' + $env.API_KEY
+ }
+});
+
+return [{json: {deleted: true, userId}}];
+```
+
+### Authentication Patterns
+
+```javascript
+// Bearer Token
+const response = await $helpers.httpRequest({
+ url: 'https://api.example.com/data',
+ headers: {
+ 'Authorization': `Bearer ${$env.API_TOKEN}`
+ }
+});
+```
+
+```javascript
+// API Key in Header
+const response = await $helpers.httpRequest({
+ url: 'https://api.example.com/data',
+ headers: {
+ 'X-API-Key': $env.API_KEY
+ }
+});
+```
+
+```javascript
+// Basic Auth (manual)
+const credentials = Buffer.from(`${username}:${password}`).toString('base64');
+
+const response = await $helpers.httpRequest({
+ url: 'https://api.example.com/data',
+ headers: {
+ 'Authorization': `Basic ${credentials}`
+ }
+});
+```
+
+### Error Handling
+
+```javascript
+// Handle HTTP errors gracefully
+try {
+ const response = await $helpers.httpRequest({
+ method: 'GET',
+ url: 'https://api.example.com/users',
+ simple: false // Don't throw on 4xx/5xx
+ });
+
+ if (response.statusCode >= 200 && response.statusCode < 300) {
+ return [{json: {success: true, data: response.body}}];
+ } else {
+ return [{
+ json: {
+ success: false,
+ status: response.statusCode,
+ error: response.body
+ }
+ }];
+ }
+} catch (error) {
+ return [{
+ json: {
+ success: false,
+ error: error.message
+ }
+ }];
+}
+```
+
+### Full Response Access
+
+```javascript
+// Get full response including headers and status
+const response = await $helpers.httpRequest({
+ url: 'https://api.example.com/data',
+ resolveWithFullResponse: true
+});
+
+return [{
+ json: {
+ statusCode: response.statusCode,
+ headers: response.headers,
+ body: response.body,
+ rateLimit: response.headers['x-ratelimit-remaining']
+ }
+}];
+```
+
+---
+
+## 2. DateTime (Luxon) - Date & Time Operations
+
+n8n includes Luxon for powerful date/time handling. Access via `DateTime` global.
+
+### Current Date/Time
+
+```javascript
+// Current time
+const now = DateTime.now();
+
+// Current time in specific timezone
+const nowTokyo = DateTime.now().setZone('Asia/Tokyo');
+
+// Today at midnight
+const today = DateTime.now().startOf('day');
+
+return [{
+ json: {
+ iso: now.toISO(), // "2025-01-20T15:30:00.000Z"
+ formatted: now.toFormat('yyyy-MM-dd HH:mm:ss'), // "2025-01-20 15:30:00"
+ unix: now.toSeconds(), // Unix timestamp
+ millis: now.toMillis() // Milliseconds since epoch
+ }
+}];
+```
+
+### Formatting Dates
+
+```javascript
+const now = DateTime.now();
+
+return [{
+ json: {
+ isoFormat: now.toISO(), // ISO 8601: "2025-01-20T15:30:00.000Z"
+ sqlFormat: now.toSQL(), // SQL: "2025-01-20 15:30:00.000"
+ httpFormat: now.toHTTP(), // HTTP: "Mon, 20 Jan 2025 15:30:00 GMT"
+
+ // Custom formats
+ dateOnly: now.toFormat('yyyy-MM-dd'), // "2025-01-20"
+ timeOnly: now.toFormat('HH:mm:ss'), // "15:30:00"
+ readable: now.toFormat('MMMM dd, yyyy'), // "January 20, 2025"
+ compact: now.toFormat('yyyyMMdd'), // "20250120"
+ withDay: now.toFormat('EEEE, MMMM dd, yyyy'), // "Monday, January 20, 2025"
+ custom: now.toFormat('dd/MM/yy HH:mm') // "20/01/25 15:30"
+ }
+}];
+```
+
+### Parsing Dates
+
+```javascript
+// From ISO string
+const dt1 = DateTime.fromISO('2025-01-20T15:30:00');
+
+// From specific format
+const dt2 = DateTime.fromFormat('01/20/2025', 'MM/dd/yyyy');
+
+// From SQL
+const dt3 = DateTime.fromSQL('2025-01-20 15:30:00');
+
+// From Unix timestamp
+const dt4 = DateTime.fromSeconds(1737384600);
+
+// From milliseconds
+const dt5 = DateTime.fromMillis(1737384600000);
+
+return [{json: {parsed: dt1.toISO()}}];
+```
+
+### Date Arithmetic
+
+```javascript
+const now = DateTime.now();
+
+return [{
+ json: {
+ // Adding time
+ tomorrow: now.plus({days: 1}).toISO(),
+ nextWeek: now.plus({weeks: 1}).toISO(),
+ nextMonth: now.plus({months: 1}).toISO(),
+ inTwoHours: now.plus({hours: 2}).toISO(),
+
+ // Subtracting time
+ yesterday: now.minus({days: 1}).toISO(),
+ lastWeek: now.minus({weeks: 1}).toISO(),
+ lastMonth: now.minus({months: 1}).toISO(),
+ twoHoursAgo: now.minus({hours: 2}).toISO(),
+
+ // Complex operations
+ in90Days: now.plus({days: 90}).toFormat('yyyy-MM-dd'),
+ in6Months: now.plus({months: 6}).toFormat('yyyy-MM-dd')
+ }
+}];
+```
+
+### Time Comparisons
+
+```javascript
+const now = DateTime.now();
+const targetDate = DateTime.fromISO('2025-12-31');
+
+return [{
+ json: {
+ // Comparisons
+ isFuture: targetDate > now,
+ isPast: targetDate < now,
+ isEqual: targetDate.equals(now),
+
+ // Differences
+ daysUntil: targetDate.diff(now, 'days').days,
+ hoursUntil: targetDate.diff(now, 'hours').hours,
+ monthsUntil: targetDate.diff(now, 'months').months,
+
+ // Detailed difference
+ detailedDiff: targetDate.diff(now, ['months', 'days', 'hours']).toObject()
+ }
+}];
+```
+
+### Timezone Operations
+
+```javascript
+const now = DateTime.now();
+
+return [{
+ json: {
+ // Current timezone
+ local: now.toISO(),
+
+ // Convert to different timezone
+ tokyo: now.setZone('Asia/Tokyo').toISO(),
+ newYork: now.setZone('America/New_York').toISO(),
+ london: now.setZone('Europe/London').toISO(),
+ utc: now.toUTC().toISO(),
+
+ // Get timezone info
+ timezone: now.zoneName, // "America/Los_Angeles"
+ offset: now.offset, // Offset in minutes
+ offsetFormatted: now.toFormat('ZZ') // "+08:00"
+ }
+}];
+```
+
+### Start/End of Period
+
+```javascript
+const now = DateTime.now();
+
+return [{
+ json: {
+ startOfDay: now.startOf('day').toISO(),
+ endOfDay: now.endOf('day').toISO(),
+ startOfWeek: now.startOf('week').toISO(),
+ endOfWeek: now.endOf('week').toISO(),
+ startOfMonth: now.startOf('month').toISO(),
+ endOfMonth: now.endOf('month').toISO(),
+ startOfYear: now.startOf('year').toISO(),
+ endOfYear: now.endOf('year').toISO()
+ }
+}];
+```
+
+### Weekday & Month Info
+
+```javascript
+const now = DateTime.now();
+
+return [{
+ json: {
+ // Day info
+ weekday: now.weekday, // 1 = Monday, 7 = Sunday
+ weekdayShort: now.weekdayShort, // "Mon"
+ weekdayLong: now.weekdayLong, // "Monday"
+ isWeekend: now.weekday > 5, // Saturday or Sunday
+
+ // Month info
+ month: now.month, // 1-12
+ monthShort: now.monthShort, // "Jan"
+ monthLong: now.monthLong, // "January"
+
+ // Year info
+ year: now.year, // 2025
+ quarter: now.quarter, // 1-4
+ daysInMonth: now.daysInMonth // 28-31
+ }
+}];
+```
+
+---
+
+## 3. $jmespath() - JSON Querying
+
+Query and transform JSON structures using JMESPath syntax.
+
+### Basic Queries
+
+```javascript
+const data = $input.first().json;
+
+// Extract specific field
+const names = $jmespath(data, 'users[*].name');
+
+// Filter array
+const adults = $jmespath(data, 'users[?age >= `18`]');
+
+// Get specific index
+const firstUser = $jmespath(data, 'users[0]');
+
+return [{json: {names, adults, firstUser}}];
+```
+
+### Advanced Queries
+
+```javascript
+const data = $input.first().json;
+
+// Sort and slice
+const top5 = $jmespath(data, 'users | sort_by(@, &score) | reverse(@) | [0:5]');
+
+// Extract nested fields
+const emails = $jmespath(data, 'users[*].contact.email');
+
+// Multi-field extraction
+const simplified = $jmespath(data, 'users[*].{name: name, email: contact.email}');
+
+// Conditional filtering
+const premium = $jmespath(data, 'users[?subscription.tier == `premium`]');
+
+return [{json: {top5, emails, simplified, premium}}];
+```
+
+### Common Patterns
+
+```javascript
+// Pattern 1: Filter and project
+const query1 = $jmespath(data, 'products[?price > `100`].{name: name, price: price}');
+
+// Pattern 2: Aggregate functions
+const query2 = $jmespath(data, 'sum(products[*].price)');
+const query3 = $jmespath(data, 'max(products[*].price)');
+const query4 = $jmespath(data, 'length(products)');
+
+// Pattern 3: Nested filtering
+const query5 = $jmespath(data, 'categories[*].products[?inStock == `true`]');
+
+return [{json: {query1, query2, query3, query4, query5}}];
+```
+
+---
+
+## 4. $getWorkflowStaticData() - Persistent Storage
+
+Store data that persists across workflow executions.
+
+### Basic Usage
+
+```javascript
+// Get static data storage
+const staticData = $getWorkflowStaticData();
+
+// Initialize counter if doesn't exist
+if (!staticData.counter) {
+ staticData.counter = 0;
+}
+
+// Increment counter
+staticData.counter++;
+
+return [{
+ json: {
+ executionCount: staticData.counter
+ }
+}];
+```
+
+### Use Cases
+
+```javascript
+// Use Case 1: Rate limiting
+const staticData = $getWorkflowStaticData();
+const now = Date.now();
+
+if (!staticData.lastRun) {
+ staticData.lastRun = now;
+ staticData.runCount = 1;
+} else {
+ const timeSinceLastRun = now - staticData.lastRun;
+
+ if (timeSinceLastRun < 60000) { // Less than 1 minute
+ return [{json: {error: 'Rate limit: wait 1 minute between runs'}}];
+ }
+
+ staticData.lastRun = now;
+ staticData.runCount++;
+}
+
+return [{json: {allowed: true, totalRuns: staticData.runCount}}];
+```
+
+```javascript
+// Use Case 2: Tracking last processed ID
+const staticData = $getWorkflowStaticData();
+const currentItems = $input.all();
+
+// Get last processed ID
+const lastId = staticData.lastProcessedId || 0;
+
+// Filter only new items
+const newItems = currentItems.filter(item => item.json.id > lastId);
+
+// Update last processed ID
+if (newItems.length > 0) {
+ staticData.lastProcessedId = Math.max(...newItems.map(item => item.json.id));
+}
+
+return newItems;
+```
+
+```javascript
+// Use Case 3: Accumulating results
+const staticData = $getWorkflowStaticData();
+
+if (!staticData.accumulated) {
+ staticData.accumulated = [];
+}
+
+// Add current items to accumulated list
+const currentData = $input.all().map(item => item.json);
+staticData.accumulated.push(...currentData);
+
+return [{
+ json: {
+ currentBatch: currentData.length,
+ totalAccumulated: staticData.accumulated.length,
+ allData: staticData.accumulated
+ }
+}];
+```
+
+---
+
+## 5. Standard JavaScript Globals
+
+### Math Object
+
+```javascript
+return [{
+ json: {
+ // Rounding
+ rounded: Math.round(3.7), // 4
+ floor: Math.floor(3.7), // 3
+ ceil: Math.ceil(3.2), // 4
+
+ // Min/Max
+ max: Math.max(1, 5, 3, 9, 2), // 9
+ min: Math.min(1, 5, 3, 9, 2), // 1
+
+ // Random
+ random: Math.random(), // 0-1
+ randomInt: Math.floor(Math.random() * 100), // 0-99
+
+ // Other
+ abs: Math.abs(-5), // 5
+ sqrt: Math.sqrt(16), // 4
+ pow: Math.pow(2, 3) // 8
+ }
+}];
+```
+
+### JSON Object
+
+```javascript
+// Parse JSON string
+const jsonString = '{"name": "John", "age": 30}';
+const parsed = JSON.parse(jsonString);
+
+// Stringify object
+const obj = {name: "John", age: 30};
+const stringified = JSON.stringify(obj);
+
+// Pretty print
+const pretty = JSON.stringify(obj, null, 2);
+
+return [{json: {parsed, stringified, pretty}}];
+```
+
+### console Object
+
+```javascript
+// Debug logging (appears in browser console, press F12)
+console.log('Processing items:', $input.all().length);
+console.log('First item:', $input.first().json);
+
+// Other console methods
+console.error('Error message');
+console.warn('Warning message');
+console.info('Info message');
+
+// Continues to return data
+return [{json: {processed: true}}];
+```
+
+### Object Methods
+
+```javascript
+const obj = {name: "John", age: 30, city: "NYC"};
+
+return [{
+ json: {
+ keys: Object.keys(obj), // ["name", "age", "city"]
+ values: Object.values(obj), // ["John", 30, "NYC"]
+ entries: Object.entries(obj), // [["name", "John"], ...]
+
+ // Check property
+ hasName: 'name' in obj, // true
+
+ // Merge objects
+ merged: Object.assign({}, obj, {country: "USA"})
+ }
+}];
+```
+
+### Array Methods
+
+```javascript
+const arr = [1, 2, 3, 4, 5];
+
+return [{
+ json: {
+ mapped: arr.map(x => x * 2), // [2, 4, 6, 8, 10]
+ filtered: arr.filter(x => x > 2), // [3, 4, 5]
+ reduced: arr.reduce((sum, x) => sum + x, 0), // 15
+ some: arr.some(x => x > 3), // true
+ every: arr.every(x => x > 0), // true
+ find: arr.find(x => x > 3), // 4
+ includes: arr.includes(3), // true
+ joined: arr.join(', ') // "1, 2, 3, 4, 5"
+ }
+}];
+```
+
+---
+
+## 6. Available Node.js Modules
+
+### crypto Module
+
+```javascript
+const crypto = require('crypto');
+
+// Hash functions
+const hash = crypto.createHash('sha256')
+ .update('my secret text')
+ .digest('hex');
+
+// MD5 hash
+const md5 = crypto.createHash('md5')
+ .update('my text')
+ .digest('hex');
+
+// Random values
+const randomBytes = crypto.randomBytes(16).toString('hex');
+
+return [{json: {hash, md5, randomBytes}}];
+```
+
+### Buffer (built-in)
+
+```javascript
+// Base64 encoding
+const encoded = Buffer.from('Hello World').toString('base64');
+
+// Base64 decoding
+const decoded = Buffer.from(encoded, 'base64').toString();
+
+// Hex encoding
+const hex = Buffer.from('Hello').toString('hex');
+
+return [{json: {encoded, decoded, hex}}];
+```
+
+### URL / URLSearchParams
+
+```javascript
+// Parse URL
+const url = new URL('https://example.com/path?param1=value1¶m2=value2');
+
+// Build query string
+const params = new URLSearchParams({
+ search: 'query',
+ page: 1,
+ limit: 10
+});
+
+return [{
+ json: {
+ host: url.host,
+ pathname: url.pathname,
+ search: url.search,
+ queryString: params.toString() // "search=query&page=1&limit=10"
+ }
+}];
+```
+
+---
+
+## What's NOT Available
+
+**External npm packages are NOT available:**
+- ❌ axios
+- ❌ lodash
+- ❌ moment (use DateTime/Luxon instead)
+- ❌ request
+- ❌ Any other npm package
+
+**Workaround**: Use $helpers.httpRequest() for HTTP, or add data to workflow via HTTP Request node.
+
+---
+
+## Summary
+
+**Most Useful Built-ins**:
+1. **$helpers.httpRequest()** - API calls without HTTP Request node
+2. **DateTime** - Professional date/time handling
+3. **$jmespath()** - Complex JSON queries
+4. **Math, JSON, Object, Array** - Standard JavaScript utilities
+
+**Common Patterns**:
+- API calls: Use $helpers.httpRequest()
+- Date operations: Use DateTime (Luxon)
+- Data filtering: Use $jmespath() or JavaScript .filter()
+- Persistent data: Use $getWorkflowStaticData()
+- Hashing: Use crypto module
+
+**See Also**:
+- [SKILL.md](SKILL.md) - Overview
+- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Real usage examples
+- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Error prevention
diff --git a/docs/skills/n8n-automation/javascript-code/COMMON_PATTERNS.md b/docs/skills/n8n-automation/javascript-code/COMMON_PATTERNS.md
new file mode 100644
index 0000000..15aa06d
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/COMMON_PATTERNS.md
@@ -0,0 +1,1110 @@
+# Common Patterns - JavaScript Code Node
+
+Production-tested patterns for n8n Code nodes. These patterns are proven in real workflows.
+
+---
+
+## Overview
+
+This guide covers the 10 most useful Code node patterns for n8n workflows. Each pattern includes:
+- **Use Case**: When to use this pattern
+- **Key Techniques**: Important coding techniques demonstrated
+- **Complete Example**: Working code you can adapt
+- **Variations**: Common modifications
+
+**Pattern Categories:**
+- Data Aggregation (Patterns 1, 5, 10)
+- Content Processing (Patterns 2, 3)
+- Data Validation & Comparison (Patterns 4)
+- Data Transformation (Patterns 5, 6, 7)
+- Output Formatting (Pattern 8)
+- Filtering & Ranking (Pattern 9)
+
+---
+
+## Pattern 1: Multi-Source Data Aggregation
+
+**Use Case**: Combining data from multiple APIs, RSS feeds, webhooks, or databases
+
+**When to use:**
+- Collecting data from multiple services
+- Normalizing different API response formats
+- Merging data sources into unified structure
+- Building aggregated reports
+
+**Key Techniques**: Loop iteration, conditional parsing, data normalization
+
+### Complete Example
+
+```javascript
+// Process and structure data collected from multiple sources
+const allItems = $input.all();
+let processedArticles = [];
+
+// Handle different source formats
+for (const item of allItems) {
+ const sourceName = item.json.name || 'Unknown';
+ const sourceData = item.json;
+
+ // Parse source-specific structure - Hacker News format
+ if (sourceName === 'Hacker News' && sourceData.hits) {
+ for (const hit of sourceData.hits) {
+ processedArticles.push({
+ title: hit.title,
+ url: hit.url,
+ summary: hit.story_text || 'No summary',
+ source: 'Hacker News',
+ score: hit.points || 0,
+ fetchedAt: new Date().toISOString()
+ });
+ }
+ }
+
+ // Parse source-specific structure - Reddit format
+ else if (sourceName === 'Reddit' && sourceData.data?.children) {
+ for (const post of sourceData.data.children) {
+ processedArticles.push({
+ title: post.data.title,
+ url: post.data.url,
+ summary: post.data.selftext || 'No summary',
+ source: 'Reddit',
+ score: post.data.score || 0,
+ fetchedAt: new Date().toISOString()
+ });
+ }
+ }
+
+ // Parse source-specific structure - RSS feed format
+ else if (sourceName === 'RSS' && sourceData.items) {
+ for (const rssItem of sourceData.items) {
+ processedArticles.push({
+ title: rssItem.title,
+ url: rssItem.link,
+ summary: rssItem.description || 'No summary',
+ source: 'RSS Feed',
+ score: 0,
+ fetchedAt: new Date().toISOString()
+ });
+ }
+ }
+}
+
+// Sort by score (highest first)
+processedArticles.sort((a, b) => b.score - a.score);
+
+return processedArticles.map(article => ({json: article}));
+```
+
+### Variations
+
+```javascript
+// Variation 1: Add source weighting
+for (const article of processedArticles) {
+ const weights = {
+ 'Hacker News': 1.5,
+ 'Reddit': 1.0,
+ 'RSS Feed': 0.8
+ };
+
+ article.weightedScore = article.score * (weights[article.source] || 1.0);
+}
+
+// Variation 2: Filter by minimum score
+processedArticles = processedArticles.filter(article => article.score >= 10);
+
+// Variation 3: Deduplicate by URL
+const seen = new Set();
+processedArticles = processedArticles.filter(article => {
+ if (seen.has(article.url)) {
+ return false;
+ }
+ seen.add(article.url);
+ return true;
+});
+```
+
+---
+
+## Pattern 2: Regex Filtering & Pattern Matching
+
+**Use Case**: Content analysis, keyword extraction, mention tracking, text parsing
+
+**When to use:**
+- Extracting mentions or tags from text
+- Finding patterns in unstructured data
+- Counting keyword occurrences
+- Validating formats (emails, phone numbers)
+
+**Key Techniques**: Regex matching, object aggregation, sorting/ranking
+
+### Complete Example
+
+```javascript
+// Extract and track mentions using regex patterns
+const etfPattern = /\b([A-Z]{2,5})\b/g;
+const knownETFs = ['VOO', 'VTI', 'VT', 'SCHD', 'QYLD', 'VXUS', 'SPY', 'QQQ'];
+
+const etfMentions = {};
+
+for (const item of $input.all()) {
+ const data = item.json.data;
+
+ // Skip if no data or children
+ if (!data?.children) continue;
+
+ for (const post of data.children) {
+ // Combine title and body text
+ const title = post.data.title || '';
+ const body = post.data.selftext || '';
+ const combinedText = (title + ' ' + body).toUpperCase();
+
+ // Find all matches
+ const matches = combinedText.match(etfPattern);
+
+ if (matches) {
+ for (const match of matches) {
+ // Only count known ETFs
+ if (knownETFs.includes(match)) {
+ if (!etfMentions[match]) {
+ etfMentions[match] = {
+ count: 0,
+ totalScore: 0,
+ posts: []
+ };
+ }
+
+ etfMentions[match].count++;
+ etfMentions[match].totalScore += post.data.score || 0;
+ etfMentions[match].posts.push({
+ title: post.data.title,
+ url: post.data.url,
+ score: post.data.score
+ });
+ }
+ }
+ }
+ }
+}
+
+// Convert to array and sort by mention count
+return Object.entries(etfMentions)
+ .map(([etf, data]) => ({
+ json: {
+ etf,
+ mentions: data.count,
+ totalScore: data.totalScore,
+ averageScore: data.totalScore / data.count,
+ topPosts: data.posts
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 3)
+ }
+ }))
+ .sort((a, b) => b.json.mentions - a.json.mentions);
+```
+
+### Variations
+
+```javascript
+// Variation 1: Email extraction
+const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
+const emails = text.match(emailPattern) || [];
+
+// Variation 2: Phone number extraction
+const phonePattern = /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g;
+const phones = text.match(phonePattern) || [];
+
+// Variation 3: Hashtag extraction
+const hashtagPattern = /#(\w+)/g;
+const hashtags = [];
+let match;
+while ((match = hashtagPattern.exec(text)) !== null) {
+ hashtags.push(match[1]);
+}
+
+// Variation 4: URL extraction
+const urlPattern = /https?:\/\/[^\s]+/g;
+const urls = text.match(urlPattern) || [];
+```
+
+---
+
+## Pattern 3: Markdown Parsing & Structured Data Extraction
+
+**Use Case**: Parsing formatted text, extracting structured fields, content transformation
+
+**When to use:**
+- Parsing markdown or HTML
+- Extracting data from structured text
+- Converting formatted content to JSON
+- Processing documentation or articles
+
+**Key Techniques**: Regex grouping, helper functions, data normalization, while loops for iteration
+
+### Complete Example
+
+```javascript
+// Parse markdown and extract structured information
+const markdown = $input.first().json.data.markdown;
+const adRegex = /##\s*(.*?)\n(.*?)(?=\n##|\n---|$)/gs;
+
+const ads = [];
+let match;
+
+// Helper function to parse time strings to minutes
+function parseTimeToMinutes(timeStr) {
+ if (!timeStr) return 999999; // Sort unparseable times last
+
+ const hourMatch = timeStr.match(/(\d+)\s*hour/);
+ const dayMatch = timeStr.match(/(\d+)\s*day/);
+ const minMatch = timeStr.match(/(\d+)\s*min/);
+
+ let totalMinutes = 0;
+ if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 1440; // 24 * 60
+ if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
+ if (minMatch) totalMinutes += parseInt(minMatch[1]);
+
+ return totalMinutes;
+}
+
+// Extract all job postings from markdown
+while ((match = adRegex.exec(markdown)) !== null) {
+ const title = match[1]?.trim() || 'No title';
+ const content = match[2]?.trim() || '';
+
+ // Extract structured fields from content
+ const districtMatch = content.match(/\*\*District:\*\*\s*(.*?)(?:\n|$)/);
+ const salaryMatch = content.match(/\*\*Salary:\*\*\s*(.*?)(?:\n|$)/);
+ const timeMatch = content.match(/Posted:\s*(.*?)\*/);
+
+ ads.push({
+ title: title,
+ district: districtMatch?.[1].trim() || 'Unknown',
+ salary: salaryMatch?.[1].trim() || 'Not specified',
+ postedTimeAgo: timeMatch?.[1] || 'Unknown',
+ timeInMinutes: parseTimeToMinutes(timeMatch?.[1]),
+ fullContent: content,
+ extractedAt: new Date().toISOString()
+ });
+}
+
+// Sort by recency (posted time)
+ads.sort((a, b) => a.timeInMinutes - b.timeInMinutes);
+
+return ads.map(ad => ({json: ad}));
+```
+
+### Variations
+
+```javascript
+// Variation 1: Parse HTML table to JSON
+const tableRegex = /
(.*?)<\/tr>/gs;
+const cellRegex = /(.*?)<\/td>/g;
+
+const rows = [];
+let tableMatch;
+
+while ((tableMatch = tableRegex.exec(htmlTable)) !== null) {
+ const cells = [];
+ let cellMatch;
+
+ while ((cellMatch = cellRegex.exec(tableMatch[1])) !== null) {
+ cells.push(cellMatch[1].trim());
+ }
+
+ if (cells.length > 0) {
+ rows.push(cells);
+ }
+}
+
+// Variation 2: Extract code blocks from markdown
+const codeBlockRegex = /```(\w+)?\n(.*?)```/gs;
+const codeBlocks = [];
+
+while ((match = codeBlockRegex.exec(markdown)) !== null) {
+ codeBlocks.push({
+ language: match[1] || 'plain',
+ code: match[2].trim()
+ });
+}
+
+// Variation 3: Parse YAML frontmatter
+const frontmatterRegex = /^---\n(.*?)\n---/s;
+const frontmatterMatch = content.match(frontmatterRegex);
+
+if (frontmatterMatch) {
+ const yamlLines = frontmatterMatch[1].split('\n');
+ const metadata = {};
+
+ for (const line of yamlLines) {
+ const [key, ...valueParts] = line.split(':');
+ if (key && valueParts.length > 0) {
+ metadata[key.trim()] = valueParts.join(':').trim();
+ }
+ }
+}
+```
+
+---
+
+## Pattern 4: JSON Comparison & Validation
+
+**Use Case**: Workflow versioning, configuration validation, change detection, data integrity
+
+**When to use:**
+- Comparing two versions of data
+- Detecting changes in configurations
+- Validating data consistency
+- Checking for differences
+
+**Key Techniques**: JSON ordering, base64 decoding, deep comparison, object manipulation
+
+### Complete Example
+
+```javascript
+// Compare and validate JSON objects from different sources
+const orderJsonKeys = (jsonObj) => {
+ const ordered = {};
+ Object.keys(jsonObj).sort().forEach(key => {
+ ordered[key] = jsonObj[key];
+ });
+ return ordered;
+};
+
+const allItems = $input.all();
+
+// Assume first item is base64-encoded original, second is current
+const origWorkflow = JSON.parse(
+ Buffer.from(allItems[0].json.content, 'base64').toString()
+);
+const currentWorkflow = allItems[1].json;
+
+// Order keys for consistent comparison
+const orderedOriginal = orderJsonKeys(origWorkflow);
+const orderedCurrent = orderJsonKeys(currentWorkflow);
+
+// Deep comparison
+const isSame = JSON.stringify(orderedOriginal) === JSON.stringify(orderedCurrent);
+
+// Find differences
+const differences = [];
+for (const key of Object.keys(orderedOriginal)) {
+ if (JSON.stringify(orderedOriginal[key]) !== JSON.stringify(orderedCurrent[key])) {
+ differences.push({
+ field: key,
+ original: orderedOriginal[key],
+ current: orderedCurrent[key]
+ });
+ }
+}
+
+// Check for new keys
+for (const key of Object.keys(orderedCurrent)) {
+ if (!(key in orderedOriginal)) {
+ differences.push({
+ field: key,
+ original: null,
+ current: orderedCurrent[key],
+ status: 'new'
+ });
+ }
+}
+
+return [{
+ json: {
+ identical: isSame,
+ differenceCount: differences.length,
+ differences: differences,
+ original: orderedOriginal,
+ current: orderedCurrent,
+ comparedAt: new Date().toISOString()
+ }
+}];
+```
+
+### Variations
+
+```javascript
+// Variation 1: Simple equality check
+const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2);
+
+// Variation 2: Deep diff with detailed changes
+function deepDiff(obj1, obj2, path = '') {
+ const changes = [];
+
+ for (const key in obj1) {
+ const currentPath = path ? `${path}.${key}` : key;
+
+ if (!(key in obj2)) {
+ changes.push({type: 'removed', path: currentPath, value: obj1[key]});
+ } else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
+ changes.push(...deepDiff(obj1[key], obj2[key], currentPath));
+ } else if (obj1[key] !== obj2[key]) {
+ changes.push({
+ type: 'modified',
+ path: currentPath,
+ from: obj1[key],
+ to: obj2[key]
+ });
+ }
+ }
+
+ for (const key in obj2) {
+ if (!(key in obj1)) {
+ const currentPath = path ? `${path}.${key}` : key;
+ changes.push({type: 'added', path: currentPath, value: obj2[key]});
+ }
+ }
+
+ return changes;
+}
+
+// Variation 3: Schema validation
+function validateSchema(data, schema) {
+ const errors = [];
+
+ for (const field of schema.required || []) {
+ if (!(field in data)) {
+ errors.push(`Missing required field: ${field}`);
+ }
+ }
+
+ for (const [field, type] of Object.entries(schema.types || {})) {
+ if (field in data && typeof data[field] !== type) {
+ errors.push(`Field ${field} should be ${type}, got ${typeof data[field]}`);
+ }
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors
+ };
+}
+```
+
+---
+
+## Pattern 5: CRM Data Transformation
+
+**Use Case**: Lead enrichment, data normalization, API preparation, form data processing
+
+**When to use:**
+- Processing form submissions
+- Preparing data for CRM APIs
+- Normalizing contact information
+- Enriching lead data
+
+**Key Techniques**: Object destructuring, data mapping, format conversion, field splitting
+
+### Complete Example
+
+```javascript
+// Transform form data into CRM-compatible format
+const item = $input.all()[0];
+const {
+ name,
+ email,
+ phone,
+ company,
+ course_interest,
+ message,
+ timestamp
+} = item.json;
+
+// Split name into first and last
+const nameParts = name.split(' ');
+const firstName = nameParts[0] || '';
+const lastName = nameParts.slice(1).join(' ') || 'Unknown';
+
+// Format phone number
+const cleanPhone = phone.replace(/[^\d]/g, ''); // Remove non-digits
+
+// Build CRM data structure
+const crmData = {
+ data: {
+ type: 'Contact',
+ attributes: {
+ first_name: firstName,
+ last_name: lastName,
+ email1: email,
+ phone_work: cleanPhone,
+ account_name: company,
+ description: `Course Interest: ${course_interest}\n\nMessage: ${message}\n\nSubmitted: ${timestamp}`,
+ lead_source: 'Website Form',
+ status: 'New'
+ }
+ },
+ metadata: {
+ original_submission: timestamp,
+ processed_at: new Date().toISOString()
+ }
+};
+
+return [{
+ json: {
+ ...item.json,
+ crmData,
+ processed: true
+ }
+}];
+```
+
+### Variations
+
+```javascript
+// Variation 1: Multiple contact processing
+const contacts = $input.all();
+
+return contacts.map(item => {
+ const data = item.json;
+ const [firstName, ...lastNameParts] = data.name.split(' ');
+
+ return {
+ json: {
+ firstName,
+ lastName: lastNameParts.join(' ') || 'Unknown',
+ email: data.email.toLowerCase(),
+ phone: data.phone.replace(/[^\d]/g, ''),
+ tags: [data.source, data.interest_level].filter(Boolean)
+ }
+ };
+});
+
+// Variation 2: Field validation and normalization
+function normalizePContact(raw) {
+ return {
+ first_name: raw.firstName?.trim() || '',
+ last_name: raw.lastName?.trim() || 'Unknown',
+ email: raw.email?.toLowerCase().trim() || '',
+ phone: raw.phone?.replace(/[^\d]/g, '') || '',
+ company: raw.company?.trim() || 'Unknown',
+ title: raw.title?.trim() || '',
+ valid: Boolean(raw.email && raw.firstName)
+ };
+}
+
+// Variation 3: Lead scoring
+function calculateLeadScore(data) {
+ let score = 0;
+
+ if (data.email) score += 10;
+ if (data.phone) score += 10;
+ if (data.company) score += 15;
+ if (data.title?.toLowerCase().includes('director')) score += 20;
+ if (data.title?.toLowerCase().includes('manager')) score += 15;
+ if (data.message?.length > 100) score += 10;
+
+ return score;
+}
+```
+
+---
+
+## Pattern 6: Release Information Processing
+
+**Use Case**: Version management, changelog parsing, release notes generation, GitHub API processing
+
+**When to use:**
+- Processing GitHub releases
+- Filtering stable versions
+- Generating changelog summaries
+- Extracting version information
+
+**Key Techniques**: Array filtering, conditional field extraction, date formatting, string manipulation
+
+### Complete Example
+
+```javascript
+// Extract and filter stable releases from GitHub API
+const allReleases = $input.first().json;
+
+const stableReleases = allReleases
+ .filter(release => !release.prerelease && !release.draft)
+ .slice(0, 10)
+ .map(release => {
+ // Extract highlights section from changelog
+ const body = release.body || '';
+ let highlights = 'No highlights available';
+
+ if (body.includes('## Highlights:')) {
+ highlights = body.split('## Highlights:')[1]?.split('##')[0]?.trim();
+ } else {
+ // Fallback to first 500 chars
+ highlights = body.substring(0, 500) + '...';
+ }
+
+ return {
+ tag: release.tag_name,
+ name: release.name,
+ published: release.published_at,
+ publishedDate: new Date(release.published_at).toLocaleDateString(),
+ author: release.author.login,
+ url: release.html_url,
+ changelog: body,
+ highlights: highlights,
+ assetCount: release.assets.length,
+ assets: release.assets.map(asset => ({
+ name: asset.name,
+ size: asset.size,
+ downloadCount: asset.download_count,
+ downloadUrl: asset.browser_download_url
+ }))
+ };
+ });
+
+return stableReleases.map(release => ({json: release}));
+```
+
+### Variations
+
+```javascript
+// Variation 1: Version comparison
+function compareVersions(v1, v2) {
+ const parts1 = v1.replace('v', '').split('.').map(Number);
+ const parts2 = v2.replace('v', '').split('.').map(Number);
+
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
+ const num1 = parts1[i] || 0;
+ const num2 = parts2[i] || 0;
+
+ if (num1 > num2) return 1;
+ if (num1 < num2) return -1;
+ }
+
+ return 0;
+}
+
+// Variation 2: Breaking change detection
+function hasBreakingChanges(changelog) {
+ const breakingKeywords = [
+ 'BREAKING CHANGE',
+ 'breaking change',
+ 'BC:',
+ '💥'
+ ];
+
+ return breakingKeywords.some(keyword => changelog.includes(keyword));
+}
+
+// Variation 3: Extract version numbers
+const versionPattern = /v?(\d+)\.(\d+)\.(\d+)/;
+const match = tagName.match(versionPattern);
+
+if (match) {
+ const [_, major, minor, patch] = match;
+ const version = {major: parseInt(major), minor: parseInt(minor), patch: parseInt(patch)};
+}
+```
+
+---
+
+## Pattern 7: Array Transformation with Context
+
+**Use Case**: Quick data transformation, field mapping, adding computed fields
+
+**When to use:**
+- Transforming arrays with additional context
+- Adding calculated fields
+- Simplifying complex objects
+- Pluralization logic
+
+**Key Techniques**: Array methods chaining, ternary operators, computed properties
+
+### Complete Example
+
+```javascript
+// Transform releases with contextual information
+const releases = $input.first().json
+ .filter(release => !release.prerelease && !release.draft)
+ .slice(0, 10)
+ .map(release => ({
+ version: release.tag_name,
+ assetCount: release.assets.length,
+ assetsCountText: release.assets.length === 1 ? 'file' : 'files',
+ downloadUrl: release.html_url,
+ isRecent: new Date(release.published_at) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
+ age: Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000)),
+ ageText: `${Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000))} days ago`
+ }));
+
+return releases.map(release => ({json: release}));
+```
+
+### Variations
+
+```javascript
+// Variation 1: Add ranking
+const items = $input.all()
+ .sort((a, b) => b.json.score - a.json.score)
+ .map((item, index) => ({
+ json: {
+ ...item.json,
+ rank: index + 1,
+ medal: index < 3 ? ['🥇', '🥈', '🥉'][index] : ''
+ }
+ }));
+
+// Variation 2: Add percentage calculations
+const total = $input.all().reduce((sum, item) => sum + item.json.value, 0);
+
+const itemsWithPercentage = $input.all().map(item => ({
+ json: {
+ ...item.json,
+ percentage: ((item.json.value / total) * 100).toFixed(2) + '%'
+ }
+}));
+
+// Variation 3: Add category labels
+const categorize = (value) => {
+ if (value > 100) return 'High';
+ if (value > 50) return 'Medium';
+ return 'Low';
+};
+
+const categorized = $input.all().map(item => ({
+ json: {
+ ...item.json,
+ category: categorize(item.json.value)
+ }
+}));
+```
+
+---
+
+## Pattern 8: Slack Block Kit Formatting
+
+**Use Case**: Chat notifications, rich message formatting, interactive messages
+
+**When to use:**
+- Sending formatted Slack messages
+- Creating interactive notifications
+- Building rich content for chat platforms
+- Status reports and alerts
+
+**Key Techniques**: Template literals, nested objects, Block Kit syntax, date formatting
+
+### Complete Example
+
+```javascript
+// Create Slack-formatted message with structured blocks
+const date = new Date().toISOString().split('T')[0];
+const data = $input.first().json;
+
+return [{
+ json: {
+ text: `Daily Report - ${date}`, // Fallback text
+ blocks: [
+ {
+ type: "header",
+ text: {
+ type: "plain_text",
+ text: `📊 Daily Security Report - ${date}`
+ }
+ },
+ {
+ type: "section",
+ text: {
+ type: "mrkdwn",
+ text: `*Status:* ${data.status === 'ok' ? '✅ All Clear' : '⚠️ Issues Detected'}\n*Alerts:* ${data.alertCount || 0}\n*Updated:* ${new Date().toLocaleString()}`
+ }
+ },
+ {
+ type: "divider"
+ },
+ {
+ type: "section",
+ fields: [
+ {
+ type: "mrkdwn",
+ text: `*Failed Logins:*\n${data.failedLogins || 0}`
+ },
+ {
+ type: "mrkdwn",
+ text: `*API Errors:*\n${data.apiErrors || 0}`
+ },
+ {
+ type: "mrkdwn",
+ text: `*Uptime:*\n${data.uptime || '100%'}`
+ },
+ {
+ type: "mrkdwn",
+ text: `*Response Time:*\n${data.avgResponseTime || 'N/A'}ms`
+ }
+ ]
+ },
+ {
+ type: "context",
+ elements: [{
+ type: "mrkdwn",
+ text: `Report generated automatically by n8n workflow`
+ }]
+ }
+ ]
+ }
+}];
+```
+
+### Variations
+
+```javascript
+// Variation 1: Interactive buttons
+const blocksWithButtons = [
+ {
+ type: "section",
+ text: {
+ type: "mrkdwn",
+ text: "Would you like to approve this request?"
+ },
+ accessory: {
+ type: "button",
+ text: {
+ type: "plain_text",
+ text: "Approve"
+ },
+ style: "primary",
+ value: "approve",
+ action_id: "approve_button"
+ }
+ }
+];
+
+// Variation 2: List formatting
+const items = ['Item 1', 'Item 2', 'Item 3'];
+const formattedList = items.map((item, i) => `${i + 1}. ${item}`).join('\n');
+
+// Variation 3: Status indicators
+function getStatusEmoji(status) {
+ const statusMap = {
+ 'success': '✅',
+ 'warning': '⚠️',
+ 'error': '❌',
+ 'info': 'ℹ️'
+ };
+
+ return statusMap[status] || '•';
+}
+
+// Variation 4: Truncate long messages
+function truncate(text, maxLength = 3000) {
+ if (text.length <= maxLength) return text;
+ return text.substring(0, maxLength - 3) + '...';
+}
+```
+
+---
+
+## Pattern 9: Top N Filtering & Ranking
+
+**Use Case**: RAG pipelines, ranking algorithms, result filtering, leaderboards
+
+**When to use:**
+- Getting top results by score
+- Filtering best/worst performers
+- Building leaderboards
+- Relevance ranking
+
+**Key Techniques**: Sorting, slicing, null coalescing, score calculations
+
+### Complete Example
+
+```javascript
+// Filter and rank by similarity score, return top results
+const ragResponse = $input.item.json;
+const chunks = ragResponse.chunks || [];
+
+// Sort by similarity (highest first)
+const topChunks = chunks
+ .sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
+ .slice(0, 6);
+
+return [{
+ json: {
+ query: ragResponse.query,
+ topChunks: topChunks,
+ count: topChunks.length,
+ maxSimilarity: topChunks[0]?.similarity || 0,
+ minSimilarity: topChunks[topChunks.length - 1]?.similarity || 0,
+ averageSimilarity: topChunks.reduce((sum, chunk) => sum + (chunk.similarity || 0), 0) / topChunks.length
+ }
+}];
+```
+
+### Variations
+
+```javascript
+// Variation 1: Top N with minimum threshold
+const threshold = 0.7;
+const topItems = $input.all()
+ .filter(item => item.json.score >= threshold)
+ .sort((a, b) => b.json.score - a.json.score)
+ .slice(0, 10);
+
+// Variation 2: Bottom N (worst performers)
+const bottomItems = $input.all()
+ .sort((a, b) => a.json.score - b.json.score) // Ascending
+ .slice(0, 5);
+
+// Variation 3: Top N by multiple criteria
+const ranked = $input.all()
+ .map(item => ({
+ ...item,
+ compositeScore: (item.json.relevance * 0.6) + (item.json.recency * 0.4)
+ }))
+ .sort((a, b) => b.compositeScore - a.compositeScore)
+ .slice(0, 10);
+
+// Variation 4: Percentile filtering
+const allScores = $input.all().map(item => item.json.score).sort((a, b) => b - a);
+const percentile95 = allScores[Math.floor(allScores.length * 0.05)];
+
+const topPercentile = $input.all().filter(item => item.json.score >= percentile95);
+```
+
+---
+
+## Pattern 10: String Aggregation & Reporting
+
+**Use Case**: Report generation, log aggregation, content concatenation, summary creation
+
+**When to use:**
+- Combining multiple text outputs
+- Generating reports from data
+- Aggregating logs or messages
+- Creating formatted summaries
+
+**Key Techniques**: Array joining, string concatenation, template literals, timestamp handling
+
+### Complete Example
+
+```javascript
+// Aggregate multiple text inputs into formatted report
+const allItems = $input.all();
+
+// Collect all messages
+const messages = allItems.map(item => item.json.message);
+
+// Build report
+const header = `🎯 **Daily Summary Report**\n📅 ${new Date().toLocaleString()}\n📊 Total Items: ${messages.length}\n\n`;
+const divider = '\n\n---\n\n';
+const footer = `\n\n---\n\n✅ Report generated at ${new Date().toISOString()}`;
+
+const finalReport = header + messages.join(divider) + footer;
+
+return [{
+ json: {
+ report: finalReport,
+ messageCount: messages.length,
+ generatedAt: new Date().toISOString(),
+ reportLength: finalReport.length
+ }
+}];
+```
+
+### Variations
+
+```javascript
+// Variation 1: Numbered list
+const numberedReport = allItems
+ .map((item, index) => `${index + 1}. ${item.json.title}\n ${item.json.description}`)
+ .join('\n\n');
+
+// Variation 2: Markdown table
+const headers = '| Name | Status | Score |\n|------|--------|-------|\n';
+const rows = allItems
+ .map(item => `| ${item.json.name} | ${item.json.status} | ${item.json.score} |`)
+ .join('\n');
+
+const table = headers + rows;
+
+// Variation 3: HTML report
+const htmlReport = `
+
+
+Report
+
+ Report - ${new Date().toLocaleDateString()}
+
+ ${allItems.map(item => `- ${item.json.title}: ${item.json.value}
`).join('\n ')}
+
+
+
+`;
+
+// Variation 4: JSON summary
+const summary = {
+ generated: new Date().toISOString(),
+ totalItems: allItems.length,
+ items: allItems.map(item => item.json),
+ statistics: {
+ total: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0),
+ average: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0) / allItems.length,
+ max: Math.max(...allItems.map(item => item.json.value || 0)),
+ min: Math.min(...allItems.map(item => item.json.value || 0))
+ }
+};
+```
+
+---
+
+## Choosing the Right Pattern
+
+### Pattern Selection Guide
+
+| Your Goal | Use Pattern |
+|-----------|-------------|
+| Combine multiple API responses | Pattern 1 (Multi-source Aggregation) |
+| Extract mentions or keywords | Pattern 2 (Regex Filtering) |
+| Parse formatted text | Pattern 3 (Markdown Parsing) |
+| Detect changes in data | Pattern 4 (JSON Comparison) |
+| Prepare form data for CRM | Pattern 5 (CRM Transformation) |
+| Process GitHub releases | Pattern 6 (Release Processing) |
+| Add computed fields | Pattern 7 (Array Transformation) |
+| Format Slack messages | Pattern 8 (Block Kit Formatting) |
+| Get top results | Pattern 9 (Top N Filtering) |
+| Create text reports | Pattern 10 (String Aggregation) |
+
+### Combining Patterns
+
+Many real workflows combine multiple patterns:
+
+```javascript
+// Example: Multi-source aggregation + Top N filtering
+const allItems = $input.all();
+const aggregated = [];
+
+// Pattern 1: Aggregate from different sources
+for (const item of allItems) {
+ // ... aggregation logic
+ aggregated.push(normalizedItem);
+}
+
+// Pattern 9: Get top 10 by score
+const top10 = aggregated
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 10);
+
+// Pattern 10: Generate report
+const report = `Top 10 Items:\n\n${top10.map((item, i) => `${i + 1}. ${item.title} (${item.score})`).join('\n')}`;
+
+return [{json: {report, items: top10}}];
+```
+
+---
+
+## Summary
+
+**Most Useful Patterns**:
+1. Multi-source Aggregation - Combining data from APIs, databases
+2. Top N Filtering - Rankings, leaderboards, best results
+3. Data Transformation - CRM data, field mapping, enrichment
+
+**Key Techniques Across Patterns**:
+- Array methods (map, filter, reduce, sort, slice)
+- Regex for pattern matching
+- Object manipulation and destructuring
+- Error handling with optional chaining
+- Template literals for formatting
+
+**See Also**:
+- [DATA_ACCESS.md](DATA_ACCESS.md) - Data access methods
+- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes
+- [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) - Built-in helpers
diff --git a/docs/skills/n8n-automation/javascript-code/DATA_ACCESS.md b/docs/skills/n8n-automation/javascript-code/DATA_ACCESS.md
new file mode 100644
index 0000000..50a8d20
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/DATA_ACCESS.md
@@ -0,0 +1,782 @@
+# Data Access Patterns - JavaScript Code Node
+
+Comprehensive guide to accessing data in n8n Code nodes using JavaScript.
+
+---
+
+## Overview
+
+In n8n Code nodes, you access data from previous nodes using built-in variables and methods. Understanding which method to use is critical for correct workflow execution.
+
+**Data Access Priority** (by common usage):
+1. **`$input.all()`** - Most common - Batch operations, aggregations
+2. **`$input.first()`** - Very common - Single item operations
+3. **`$input.item`** - Common - Each Item mode only
+4. **`$node["NodeName"].json`** - Specific node references
+5. **`$json`** - Direct current item (legacy, use `$input` instead)
+
+---
+
+## Pattern 1: $input.all() - Process All Items
+
+**Usage**: Most common pattern for batch processing
+
+**When to use:**
+- Processing multiple records
+- Aggregating data (sum, count, average)
+- Filtering arrays
+- Transforming datasets
+- Comparing items
+- Sorting or ranking
+
+### Basic Usage
+
+```javascript
+// Get all items from previous node
+const allItems = $input.all();
+
+// allItems is an array of objects like:
+// [
+// {json: {id: 1, name: "Alice"}},
+// {json: {id: 2, name: "Bob"}}
+// ]
+
+console.log(`Received ${allItems.length} items`);
+
+return allItems;
+```
+
+### Example 1: Filter Active Items
+
+```javascript
+const allItems = $input.all();
+
+// Filter only active items
+const activeItems = allItems.filter(item => item.json.status === 'active');
+
+return activeItems;
+```
+
+### Example 2: Transform All Items
+
+```javascript
+const allItems = $input.all();
+
+// Map to new structure
+const transformed = allItems.map(item => ({
+ json: {
+ id: item.json.id,
+ fullName: `${item.json.firstName} ${item.json.lastName}`,
+ email: item.json.email,
+ processedAt: new Date().toISOString()
+ }
+}));
+
+return transformed;
+```
+
+### Example 3: Aggregate Data
+
+```javascript
+const allItems = $input.all();
+
+// Calculate total
+const total = allItems.reduce((sum, item) => {
+ return sum + (item.json.amount || 0);
+}, 0);
+
+return [{
+ json: {
+ total,
+ count: allItems.length,
+ average: total / allItems.length
+ }
+}];
+```
+
+### Example 4: Sort and Limit
+
+```javascript
+const allItems = $input.all();
+
+// Get top 5 by score
+const topFive = allItems
+ .sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
+ .slice(0, 5);
+
+return topFive.map(item => ({json: item.json}));
+```
+
+### Example 5: Group By Category
+
+```javascript
+const allItems = $input.all();
+
+// Group items by category
+const grouped = {};
+
+for (const item of allItems) {
+ const category = item.json.category || 'Uncategorized';
+
+ if (!grouped[category]) {
+ grouped[category] = [];
+ }
+
+ grouped[category].push(item.json);
+}
+
+// Convert to array format
+return Object.entries(grouped).map(([category, items]) => ({
+ json: {
+ category,
+ items,
+ count: items.length
+ }
+}));
+```
+
+### Example 6: Deduplicate by ID
+
+```javascript
+const allItems = $input.all();
+
+// Remove duplicates by ID
+const seen = new Set();
+const unique = [];
+
+for (const item of allItems) {
+ const id = item.json.id;
+
+ if (!seen.has(id)) {
+ seen.add(id);
+ unique.push(item);
+ }
+}
+
+return unique;
+```
+
+---
+
+## Pattern 2: $input.first() - Get First Item
+
+**Usage**: Very common for single-item operations
+
+**When to use:**
+- Previous node returns single object
+- Working with API responses
+- Getting initial/first data point
+- Configuration or metadata access
+
+### Basic Usage
+
+```javascript
+// Get first item from previous node
+const firstItem = $input.first();
+
+// Access the JSON data
+const data = firstItem.json;
+
+console.log('First item:', data);
+
+return [{json: data}];
+```
+
+### Example 1: Process Single API Response
+
+```javascript
+// Get API response (typically single object)
+const response = $input.first().json;
+
+// Extract what you need
+return [{
+ json: {
+ userId: response.data.user.id,
+ userName: response.data.user.name,
+ status: response.status,
+ fetchedAt: new Date().toISOString()
+ }
+}];
+```
+
+### Example 2: Transform Single Object
+
+```javascript
+const data = $input.first().json;
+
+// Transform structure
+return [{
+ json: {
+ id: data.id,
+ contact: {
+ email: data.email,
+ phone: data.phone
+ },
+ address: {
+ street: data.street,
+ city: data.city,
+ zip: data.zip
+ }
+ }
+}];
+```
+
+### Example 3: Validate Single Item
+
+```javascript
+const item = $input.first().json;
+
+// Validation logic
+const isValid = item.email && item.email.includes('@');
+
+return [{
+ json: {
+ ...item,
+ valid: isValid,
+ validatedAt: new Date().toISOString()
+ }
+}];
+```
+
+### Example 4: Extract Nested Data
+
+```javascript
+const response = $input.first().json;
+
+// Navigate nested structure
+const users = response.data?.users || [];
+
+return users.map(user => ({
+ json: {
+ id: user.id,
+ name: user.profile?.name || 'Unknown',
+ email: user.contact?.email || 'no-email'
+ }
+}));
+```
+
+### Example 5: Combine with Other Methods
+
+```javascript
+// Get first item's data
+const firstData = $input.first().json;
+
+// Use it to filter all items
+const allItems = $input.all();
+const matching = allItems.filter(item =>
+ item.json.category === firstData.targetCategory
+);
+
+return matching;
+```
+
+---
+
+## Pattern 3: $input.item - Current Item (Each Item Mode)
+
+**Usage**: Common in "Run Once for Each Item" mode
+
+**When to use:**
+- Mode is set to "Run Once for Each Item"
+- Need to process items independently
+- Per-item API calls or validations
+- Item-specific error handling
+
+**IMPORTANT**: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
+
+### Basic Usage
+
+```javascript
+// In "Run Once for Each Item" mode
+const currentItem = $input.item;
+const data = currentItem.json;
+
+console.log('Processing item:', data.id);
+
+return [{
+ json: {
+ ...data,
+ processed: true
+ }
+}];
+```
+
+### Example 1: Add Processing Metadata
+
+```javascript
+const item = $input.item;
+
+return [{
+ json: {
+ ...item.json,
+ processed: true,
+ processedAt: new Date().toISOString(),
+ processingDuration: Math.random() * 1000 // Simulated duration
+ }
+}];
+```
+
+### Example 2: Per-Item Validation
+
+```javascript
+const item = $input.item;
+const data = item.json;
+
+// Validate this specific item
+const errors = [];
+
+if (!data.email) errors.push('Email required');
+if (!data.name) errors.push('Name required');
+if (data.age && data.age < 18) errors.push('Must be 18+');
+
+return [{
+ json: {
+ ...data,
+ valid: errors.length === 0,
+ errors: errors.length > 0 ? errors : undefined
+ }
+}];
+```
+
+### Example 3: Item-Specific API Call
+
+```javascript
+const item = $input.item;
+const userId = item.json.userId;
+
+// Make API call specific to this item
+const response = await $helpers.httpRequest({
+ method: 'GET',
+ url: `https://api.example.com/users/${userId}/details`
+});
+
+return [{
+ json: {
+ ...item.json,
+ details: response
+ }
+}];
+```
+
+### Example 4: Conditional Processing
+
+```javascript
+const item = $input.item;
+const data = item.json;
+
+// Process based on item type
+if (data.type === 'premium') {
+ return [{
+ json: {
+ ...data,
+ discount: 0.20,
+ tier: 'premium'
+ }
+ }];
+} else {
+ return [{
+ json: {
+ ...data,
+ discount: 0.05,
+ tier: 'standard'
+ }
+ }];
+}
+```
+
+---
+
+## Pattern 4: $node - Reference Other Nodes
+
+**Usage**: Less common, but powerful for specific scenarios
+
+**When to use:**
+- Need data from specific named node
+- Combining data from multiple nodes
+- Accessing metadata about workflow execution
+
+### Basic Usage
+
+```javascript
+// Get output from specific node
+const webhookData = $node["Webhook"].json;
+const apiData = $node["HTTP Request"].json;
+
+return [{
+ json: {
+ fromWebhook: webhookData,
+ fromAPI: apiData
+ }
+}];
+```
+
+### Example 1: Combine Multiple Sources
+
+```javascript
+// Reference multiple nodes
+const webhook = $node["Webhook"].json;
+const database = $node["Postgres"].json;
+const api = $node["HTTP Request"].json;
+
+return [{
+ json: {
+ combined: {
+ webhook: webhook.body,
+ dbRecords: database.length,
+ apiResponse: api.status
+ },
+ processedAt: new Date().toISOString()
+ }
+}];
+```
+
+### Example 2: Compare Across Nodes
+
+```javascript
+const oldData = $node["Get Old Data"].json;
+const newData = $node["Get New Data"].json;
+
+// Compare
+const changes = {
+ added: newData.filter(n => !oldData.find(o => o.id === n.id)),
+ removed: oldData.filter(o => !newData.find(n => n.id === o.id)),
+ modified: newData.filter(n => {
+ const old = oldData.find(o => o.id === n.id);
+ return old && JSON.stringify(old) !== JSON.stringify(n);
+ })
+};
+
+return [{
+ json: {
+ changes,
+ summary: {
+ added: changes.added.length,
+ removed: changes.removed.length,
+ modified: changes.modified.length
+ }
+ }
+}];
+```
+
+### Example 3: Access Node Metadata
+
+```javascript
+// Get data from specific execution path
+const ifTrueBranch = $node["IF True"].json;
+const ifFalseBranch = $node["IF False"].json;
+
+// Use whichever branch executed
+const result = ifTrueBranch || ifFalseBranch || {};
+
+return [{json: result}];
+```
+
+---
+
+## Critical: Webhook Data Structure
+
+**MOST COMMON MISTAKE**: Forgetting webhook data is nested under `.body`
+
+### The Problem
+
+Webhook node wraps all incoming data under a `body` property. This catches many developers by surprise.
+
+### Structure
+
+```javascript
+// Webhook node output structure:
+{
+ "headers": {
+ "content-type": "application/json",
+ "user-agent": "...",
+ // ... other headers
+ },
+ "params": {},
+ "query": {},
+ "body": {
+ // ← YOUR DATA IS HERE
+ "name": "Alice",
+ "email": "alice@example.com",
+ "message": "Hello!"
+ }
+}
+```
+
+### Wrong vs Right
+
+```javascript
+// ❌ WRONG: Trying to access directly
+const name = $json.name; // undefined
+const email = $json.email; // undefined
+
+// ✅ CORRECT: Access via .body
+const name = $json.body.name; // "Alice"
+const email = $json.body.email; // "alice@example.com"
+
+// ✅ CORRECT: Extract body first
+const webhookData = $json.body;
+const name = webhookData.name; // "Alice"
+const email = webhookData.email; // "alice@example.com"
+```
+
+### Example: Full Webhook Processing
+
+```javascript
+// Get webhook data from previous node
+const webhookOutput = $input.first().json;
+
+// Access the actual payload
+const payload = webhookOutput.body;
+
+// Access headers if needed
+const contentType = webhookOutput.headers['content-type'];
+
+// Access query parameters if needed
+const apiKey = webhookOutput.query.api_key;
+
+// Process the actual data
+return [{
+ json: {
+ // Data from webhook body
+ userName: payload.name,
+ userEmail: payload.email,
+ message: payload.message,
+
+ // Metadata
+ receivedAt: new Date().toISOString(),
+ contentType: contentType,
+ authenticated: !!apiKey
+ }
+}];
+```
+
+### POST Data, Query Params, and Headers
+
+```javascript
+const webhook = $input.first().json;
+
+return [{
+ json: {
+ // POST body data
+ formData: webhook.body,
+
+ // Query parameters (?key=value)
+ queryParams: webhook.query,
+
+ // HTTP headers
+ userAgent: webhook.headers['user-agent'],
+ contentType: webhook.headers['content-type'],
+
+ // Request metadata
+ method: webhook.method, // POST, GET, etc.
+ url: webhook.url
+ }
+}];
+```
+
+### Common Webhook Scenarios
+
+```javascript
+// Scenario 1: Form submission
+const formData = $json.body;
+const name = formData.name;
+const email = formData.email;
+
+// Scenario 2: JSON API webhook
+const apiPayload = $json.body;
+const eventType = apiPayload.event;
+const data = apiPayload.data;
+
+// Scenario 3: Query parameters
+const apiKey = $json.query.api_key;
+const userId = $json.query.user_id;
+
+// Scenario 4: Headers
+const authorization = $json.headers['authorization'];
+const signature = $json.headers['x-signature'];
+```
+
+---
+
+## Choosing the Right Pattern
+
+### Decision Tree
+
+```
+Do you need ALL items from previous node?
+├─ YES → Use $input.all()
+│
+└─ NO → Do you need just the FIRST item?
+ ├─ YES → Use $input.first()
+ │
+ └─ NO → Are you in "Each Item" mode?
+ ├─ YES → Use $input.item
+ │
+ └─ NO → Do you need specific node data?
+ ├─ YES → Use $node["NodeName"]
+ └─ NO → Use $input.first() (default)
+```
+
+### Quick Reference Table
+
+| Scenario | Use This | Example |
+|----------|----------|---------|
+| Sum all amounts | `$input.all()` | `allItems.reduce((sum, i) => sum + i.json.amount, 0)` |
+| Get API response | `$input.first()` | `$input.first().json.data` |
+| Process each independently | `$input.item` | `$input.item.json` (Each Item mode) |
+| Combine two nodes | `$node["Name"]` | `$node["API"].json` |
+| Filter array | `$input.all()` | `allItems.filter(i => i.json.active)` |
+| Transform single object | `$input.first()` | `{...input.first().json, new: true}` |
+| Webhook data | `$input.first()` | `$input.first().json.body` |
+
+---
+
+## Common Mistakes
+
+### Mistake 1: Using $json Without Context
+
+```javascript
+// ❌ WRONG: $json is ambiguous
+const value = $json.field;
+
+// ✅ CORRECT: Be explicit
+const value = $input.first().json.field;
+```
+
+### Mistake 2: Forgetting .json Property
+
+```javascript
+// ❌ WRONG: Trying to access fields on item object
+const items = $input.all();
+const names = items.map(item => item.name); // undefined
+
+// ✅ CORRECT: Access via .json
+const names = items.map(item => item.json.name);
+```
+
+### Mistake 3: Using $input.item in All Items Mode
+
+```javascript
+// ❌ WRONG: $input.item is undefined in "All Items" mode
+const data = $input.item.json; // Error!
+
+// ✅ CORRECT: Use appropriate method
+const data = $input.first().json; // Or $input.all()
+```
+
+### Mistake 4: Not Handling Empty Arrays
+
+```javascript
+// ❌ WRONG: Crashes if no items
+const first = $input.all()[0].json;
+
+// ✅ CORRECT: Check length first
+const items = $input.all();
+if (items.length === 0) {
+ return [];
+}
+const first = items[0].json;
+
+// ✅ ALSO CORRECT: Use $input.first()
+const first = $input.first().json; // Built-in safety
+```
+
+### Mistake 5: Modifying Original Data
+
+```javascript
+// ❌ RISKY: Mutating original
+const items = $input.all();
+items[0].json.modified = true; // Modifies original
+return items;
+
+// ✅ SAFE: Create new objects
+const items = $input.all();
+return items.map(item => ({
+ json: {
+ ...item.json,
+ modified: true
+ }
+}));
+```
+
+---
+
+## Advanced Patterns
+
+### Pattern: Pagination Handling
+
+```javascript
+const currentPage = $input.all();
+const pageNumber = $node["Set Page"].json.page || 1;
+
+// Combine with previous pages
+const allPreviousPages = $node["Accumulator"]?.json.accumulated || [];
+
+return [{
+ json: {
+ accumulated: [...allPreviousPages, ...currentPage],
+ currentPage: pageNumber,
+ totalItems: allPreviousPages.length + currentPage.length
+ }
+}];
+```
+
+### Pattern: Conditional Node Reference
+
+```javascript
+// Access different nodes based on condition
+const condition = $input.first().json.type;
+
+let data;
+if (condition === 'api') {
+ data = $node["API Response"].json;
+} else if (condition === 'database') {
+ data = $node["Database"].json;
+} else {
+ data = $node["Default"].json;
+}
+
+return [{json: data}];
+```
+
+### Pattern: Multi-Node Aggregation
+
+```javascript
+// Collect data from multiple named nodes
+const sources = ['Source1', 'Source2', 'Source3'];
+const allData = [];
+
+for (const source of sources) {
+ const nodeData = $node[source]?.json;
+ if (nodeData) {
+ allData.push({
+ source,
+ data: nodeData
+ });
+ }
+}
+
+return allData.map(item => ({json: item}));
+```
+
+---
+
+## Summary
+
+**Most Common Patterns**:
+1. `$input.all()` - Process multiple items, batch operations
+2. `$input.first()` - Single item, API responses
+3. `$input.item` - Each Item mode processing
+
+**Critical Rule**:
+- Webhook data is under `.body` property
+
+**Best Practice**:
+- Be explicit: Use `$input.first().json.field` instead of `$json.field`
+- Always check for null/undefined
+- Use appropriate method for your mode (All Items vs Each Item)
+
+**See Also**:
+- [SKILL.md](SKILL.md) - Overview and quick start
+- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Production patterns
+- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes
diff --git a/docs/skills/n8n-automation/javascript-code/ERROR_PATTERNS.md b/docs/skills/n8n-automation/javascript-code/ERROR_PATTERNS.md
new file mode 100644
index 0000000..d2ae482
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/ERROR_PATTERNS.md
@@ -0,0 +1,763 @@
+# Error Patterns - JavaScript Code Node
+
+Complete guide to avoiding the most common Code node errors.
+
+---
+
+## Overview
+
+This guide covers the **top 5 error patterns** encountered in n8n Code nodes. Understanding and avoiding these errors will save you significant debugging time.
+
+**Error Frequency**:
+1. Empty Code / Missing Return - **38% of failures**
+2. Expression Syntax Confusion - **8% of failures**
+3. Incorrect Return Wrapper - **5% of failures**
+4. Unmatched Expression Brackets - **6% of failures**
+5. Missing Null Checks - **Common runtime error**
+
+---
+
+## Error #1: Empty Code or Missing Return Statement
+
+**Frequency**: Most common error (38% of all validation failures)
+
+**What Happens**:
+- Workflow execution fails
+- Next nodes receive no data
+- Error: "Code cannot be empty" or "Code must return data"
+
+### The Problem
+
+```javascript
+// ❌ ERROR: No code at all
+// (Empty code field)
+```
+
+```javascript
+// ❌ ERROR: Code executes but doesn't return anything
+const items = $input.all();
+
+// Process items
+for (const item of items) {
+ console.log(item.json.name);
+}
+
+// Forgot to return!
+```
+
+```javascript
+// ❌ ERROR: Early return path exists, but not all paths return
+const items = $input.all();
+
+if (items.length === 0) {
+ return []; // ✅ This path returns
+}
+
+// Process items
+const processed = items.map(item => ({json: item.json}));
+
+// ❌ Forgot to return processed!
+```
+
+### The Solution
+
+```javascript
+// ✅ CORRECT: Always return data
+const items = $input.all();
+
+// Process items
+const processed = items.map(item => ({
+ json: {
+ ...item.json,
+ processed: true
+ }
+}));
+
+return processed; // ✅ Return statement present
+```
+
+```javascript
+// ✅ CORRECT: Return empty array if no items
+const items = $input.all();
+
+if (items.length === 0) {
+ return []; // Valid: empty array when no data
+}
+
+// Process and return
+return items.map(item => ({json: item.json}));
+```
+
+```javascript
+// ✅ CORRECT: All code paths return
+const items = $input.all();
+
+if (items.length === 0) {
+ return [];
+} else if (items.length === 1) {
+ return [{json: {single: true, data: items[0].json}}];
+} else {
+ return items.map(item => ({json: item.json}));
+}
+
+// All paths covered
+```
+
+### Checklist
+
+- [ ] Code field is not empty
+- [ ] Return statement exists
+- [ ] ALL code paths return data (if/else branches)
+- [ ] Return format is correct (`[{json: {...}}]`)
+- [ ] Return happens even on errors (use try-catch)
+
+---
+
+## Error #2: Expression Syntax Confusion
+
+**Frequency**: 8% of validation failures
+
+**What Happens**:
+- Syntax error in code execution
+- Error: "Unexpected token" or "Expression syntax is not valid in Code nodes"
+- Template variables not evaluated
+
+### The Problem
+
+n8n has TWO distinct syntaxes:
+1. **Expression syntax** `{{ }}` - Used in OTHER nodes (Set, IF, HTTP Request)
+2. **JavaScript** - Used in CODE nodes (no `{{ }}`)
+
+Many developers mistakenly use expression syntax inside Code nodes.
+
+```javascript
+// ❌ WRONG: Using n8n expression syntax in Code node
+const userName = "{{ $json.name }}";
+const userEmail = "{{ $json.body.email }}";
+
+return [{
+ json: {
+ name: userName,
+ email: userEmail
+ }
+}];
+
+// Result: Literal string "{{ $json.name }}", NOT the value!
+```
+
+```javascript
+// ❌ WRONG: Trying to evaluate expressions
+const value = "{{ $now.toFormat('yyyy-MM-dd') }}";
+```
+
+### The Solution
+
+```javascript
+// ✅ CORRECT: Use JavaScript directly (no {{ }})
+const userName = $json.name;
+const userEmail = $json.body.email;
+
+return [{
+ json: {
+ name: userName,
+ email: userEmail
+ }
+}];
+```
+
+```javascript
+// ✅ CORRECT: JavaScript template literals (use backticks)
+const message = `Hello, ${$json.name}! Your email is ${$json.email}`;
+
+return [{
+ json: {
+ greeting: message
+ }
+}];
+```
+
+```javascript
+// ✅ CORRECT: Direct variable access
+const item = $input.first().json;
+
+return [{
+ json: {
+ name: item.name,
+ email: item.email,
+ timestamp: new Date().toISOString() // JavaScript Date, not {{ }}
+ }
+}];
+```
+
+### Comparison Table
+
+| Context | Syntax | Example |
+|---------|--------|---------|
+| Set node | `{{ }}` expressions | `{{ $json.name }}` |
+| IF node | `{{ }}` expressions | `{{ $json.age > 18 }}` |
+| HTTP Request URL | `{{ }}` expressions | `{{ $json.userId }}` |
+| **Code node** | **JavaScript** | `$json.name` |
+| **Code node strings** | **Template literals** | `` `Hello ${$json.name}` `` |
+
+### Quick Fix Guide
+
+```javascript
+// WRONG → RIGHT conversions
+
+// ❌ "{{ $json.field }}"
+// ✅ $json.field
+
+// ❌ "{{ $now }}"
+// ✅ new Date().toISOString()
+
+// ❌ "{{ $node['HTTP Request'].json.data }}"
+// ✅ $node["HTTP Request"].json.data
+
+// ❌ `{{ $json.firstName }} {{ $json.lastName }}`
+// ✅ `${$json.firstName} ${$json.lastName}`
+```
+
+---
+
+## Error #3: Incorrect Return Wrapper Format
+
+**Frequency**: 5% of validation failures
+
+**What Happens**:
+- Error: "Return value must be an array of objects"
+- Error: "Each item must have a json property"
+- Next nodes receive malformed data
+
+### The Problem
+
+Code nodes MUST return:
+- **Array** of objects
+- Each object MUST have a **`json` property**
+
+```javascript
+// ❌ WRONG: Returning object instead of array
+return {
+ json: {
+ result: 'success'
+ }
+};
+// Missing array wrapper []
+```
+
+```javascript
+// ❌ WRONG: Returning array without json wrapper
+return [
+ {id: 1, name: 'Alice'},
+ {id: 2, name: 'Bob'}
+];
+// Missing json property
+```
+
+```javascript
+// ❌ WRONG: Returning plain value
+return "processed";
+```
+
+```javascript
+// ❌ WRONG: Returning items without mapping
+return $input.all();
+// Works if items already have json property, but not guaranteed
+```
+
+```javascript
+// ❌ WRONG: Incomplete structure
+return [{data: {result: 'success'}}];
+// Should be {json: {...}}, not {data: {...}}
+```
+
+### The Solution
+
+```javascript
+// ✅ CORRECT: Single result
+return [{
+ json: {
+ result: 'success',
+ timestamp: new Date().toISOString()
+ }
+}];
+```
+
+```javascript
+// ✅ CORRECT: Multiple results
+return [
+ {json: {id: 1, name: 'Alice'}},
+ {json: {id: 2, name: 'Bob'}},
+ {json: {id: 3, name: 'Carol'}}
+];
+```
+
+```javascript
+// ✅ CORRECT: Transforming array
+const items = $input.all();
+
+return items.map(item => ({
+ json: {
+ id: item.json.id,
+ name: item.json.name,
+ processed: true
+ }
+}));
+```
+
+```javascript
+// ✅ CORRECT: Empty result
+return [];
+// Valid when no data to return
+```
+
+```javascript
+// ✅ CORRECT: Conditional returns
+if (shouldProcess) {
+ return [{json: {result: 'processed'}}];
+} else {
+ return [];
+}
+```
+
+### Return Format Checklist
+
+- [ ] Return value is an **array** `[...]`
+- [ ] Each array element has **`json` property**
+- [ ] Structure is `[{json: {...}}]` or `[{json: {...}}, {json: {...}}]`
+- [ ] NOT `{json: {...}}` (missing array wrapper)
+- [ ] NOT `[{...}]` (missing json property)
+
+### Common Scenarios
+
+```javascript
+// Scenario 1: Single object from API
+const response = $input.first().json;
+
+// ✅ CORRECT
+return [{json: response}];
+
+// ❌ WRONG
+return {json: response};
+
+
+// Scenario 2: Array of objects
+const users = $input.all();
+
+// ✅ CORRECT
+return users.map(user => ({json: user.json}));
+
+// ❌ WRONG
+return users; // Risky - depends on existing structure
+
+
+// Scenario 3: Computed result
+const total = $input.all().reduce((sum, item) => sum + item.json.amount, 0);
+
+// ✅ CORRECT
+return [{json: {total}}];
+
+// ❌ WRONG
+return {total};
+
+
+// Scenario 4: No results
+// ✅ CORRECT
+return [];
+
+// ❌ WRONG
+return null;
+```
+
+---
+
+## Error #4: Unmatched Expression Brackets
+
+**Frequency**: 6% of validation failures
+
+**What Happens**:
+- Parsing error during save
+- Error: "Unmatched expression brackets"
+- Code appears correct but fails validation
+
+### The Problem
+
+This error typically occurs when:
+1. Strings contain unbalanced quotes
+2. Multi-line strings with special characters
+3. Template literals with nested brackets
+
+```javascript
+// ❌ WRONG: Unescaped quote in string
+const message = "It's a nice day";
+// Single quote breaks string
+```
+
+```javascript
+// ❌ WRONG: Unbalanced brackets in regex
+const pattern = /\{(\w+)\}/; // JSON storage issue
+```
+
+```javascript
+// ❌ WRONG: Multi-line string with quotes
+const html = "
+
+";
+// Quote balance issues
+```
+
+### The Solution
+
+```javascript
+// ✅ CORRECT: Escape quotes
+const message = "It\\'s a nice day";
+// Or use different quotes
+const message = "It's a nice day"; // Double quotes work
+```
+
+```javascript
+// ✅ CORRECT: Escape regex properly
+const pattern = /\\{(\\w+)\\}/;
+```
+
+```javascript
+// ✅ CORRECT: Template literals for multi-line
+const html = `
+
+`;
+// Backticks handle multi-line and quotes
+```
+
+```javascript
+// ✅ CORRECT: Escape backslashes
+const path = "C:\\\\Users\\\\Documents\\\\file.txt";
+```
+
+### Escaping Guide
+
+| Character | Escape As | Example |
+|-----------|-----------|---------|
+| Single quote in single-quoted string | `\\'` | `'It\\'s working'` |
+| Double quote in double-quoted string | `\\"` | `"She said \\"hello\\""` |
+| Backslash | `\\\\` | `"C:\\\\path"` |
+| Newline | `\\n` | `"Line 1\\nLine 2"` |
+| Tab | `\\t` | `"Column1\\tColumn2"` |
+
+### Best Practices
+
+```javascript
+// ✅ BEST: Use template literals for complex strings
+const message = `User ${name} said: "Hello!"`;
+
+// ✅ BEST: Use template literals for HTML
+const html = `
+
+ ${title}
+ ${content}
+
+`;
+
+// ✅ BEST: Use template literals for JSON
+const jsonString = `{
+ "name": "${name}",
+ "email": "${email}"
+}`;
+```
+
+---
+
+## Error #5: Missing Null Checks / Undefined Access
+
+**Frequency**: Very common runtime error
+
+**What Happens**:
+- Workflow execution stops
+- Error: "Cannot read property 'X' of undefined"
+- Error: "Cannot read property 'X' of null"
+- Crashes on missing data
+
+### The Problem
+
+```javascript
+// ❌ WRONG: No null check - crashes if user doesn't exist
+const email = item.json.user.email;
+```
+
+```javascript
+// ❌ WRONG: Assumes array has items
+const firstItem = $input.all()[0].json;
+```
+
+```javascript
+// ❌ WRONG: Assumes nested property exists
+const city = $json.address.city;
+```
+
+```javascript
+// ❌ WRONG: No validation before array operations
+const names = $json.users.map(user => user.name);
+```
+
+### The Solution
+
+```javascript
+// ✅ CORRECT: Optional chaining
+const email = item.json?.user?.email || 'no-email@example.com';
+```
+
+```javascript
+// ✅ CORRECT: Check array length
+const items = $input.all();
+
+if (items.length === 0) {
+ return [];
+}
+
+const firstItem = items[0].json;
+```
+
+```javascript
+// ✅ CORRECT: Guard clauses
+const data = $input.first().json;
+
+if (!data.address) {
+ return [{json: {error: 'No address provided'}}];
+}
+
+const city = data.address.city;
+```
+
+```javascript
+// ✅ CORRECT: Default values
+const users = $json.users || [];
+const names = users.map(user => user.name || 'Unknown');
+```
+
+```javascript
+// ✅ CORRECT: Try-catch for risky operations
+try {
+ const email = item.json.user.email.toLowerCase();
+ return [{json: {email}}];
+} catch (error) {
+ return [{
+ json: {
+ error: 'Invalid user data',
+ details: error.message
+ }
+ }];
+}
+```
+
+### Safe Access Patterns
+
+```javascript
+// Pattern 1: Optional chaining (modern, recommended)
+const value = data?.nested?.property?.value;
+
+// Pattern 2: Logical OR with default
+const value = data.property || 'default';
+
+// Pattern 3: Ternary check
+const value = data.property ? data.property : 'default';
+
+// Pattern 4: Guard clause
+if (!data.property) {
+ return [];
+}
+const value = data.property;
+
+// Pattern 5: Try-catch
+try {
+ const value = data.nested.property.value;
+} catch (error) {
+ const value = 'default';
+}
+```
+
+### Webhook Data Safety
+
+```javascript
+// Webhook data requires extra safety
+
+// ❌ RISKY: Assumes all fields exist
+const name = $json.body.user.name;
+const email = $json.body.user.email;
+
+// ✅ SAFE: Check each level
+const body = $json.body || {};
+const user = body.user || {};
+const name = user.name || 'Unknown';
+const email = user.email || 'no-email';
+
+// ✅ BETTER: Optional chaining
+const name = $json.body?.user?.name || 'Unknown';
+const email = $json.body?.user?.email || 'no-email';
+```
+
+### Array Safety
+
+```javascript
+// ❌ RISKY: No length check
+const items = $input.all();
+const firstId = items[0].json.id;
+
+// ✅ SAFE: Check length
+const items = $input.all();
+
+if (items.length > 0) {
+ const firstId = items[0].json.id;
+} else {
+ // Handle empty case
+ return [];
+}
+
+// ✅ BETTER: Use $input.first()
+const firstItem = $input.first();
+const firstId = firstItem.json.id; // Built-in safety
+```
+
+### Object Property Safety
+
+```javascript
+// ❌ RISKY: Direct access
+const config = $json.settings.advanced.timeout;
+
+// ✅ SAFE: Step by step with defaults
+const settings = $json.settings || {};
+const advanced = settings.advanced || {};
+const timeout = advanced.timeout || 30000;
+
+// ✅ BETTER: Optional chaining
+const timeout = $json.settings?.advanced?.timeout ?? 30000;
+// Note: ?? (nullish coalescing) vs || (logical OR)
+```
+
+---
+
+## Error Prevention Checklist
+
+Use this checklist before deploying Code nodes:
+
+### Code Structure
+- [ ] Code field is not empty
+- [ ] Return statement exists
+- [ ] All code paths return data
+
+### Return Format
+- [ ] Returns array: `[...]`
+- [ ] Each item has `json` property: `{json: {...}}`
+- [ ] Format is `[{json: {...}}]`
+
+### Syntax
+- [ ] No `{{ }}` expression syntax (use JavaScript)
+- [ ] Template literals use backticks: `` `${variable}` ``
+- [ ] All quotes and brackets balanced
+- [ ] Strings properly escaped
+
+### Data Safety
+- [ ] Null checks for optional properties
+- [ ] Array length checks before access
+- [ ] Webhook data accessed via `.body`
+- [ ] Try-catch for risky operations
+- [ ] Default values for missing data
+
+### Testing
+- [ ] Test with empty input
+- [ ] Test with missing fields
+- [ ] Test with unexpected data types
+- [ ] Check browser console for errors
+
+---
+
+## Quick Error Reference
+
+| Error Message | Likely Cause | Fix |
+|---------------|--------------|-----|
+| "Code cannot be empty" | Empty code field | Add meaningful code |
+| "Code must return data" | Missing return statement | Add `return [...]` |
+| "Return value must be an array" | Returning object instead of array | Wrap in `[...]` |
+| "Each item must have json property" | Missing `json` wrapper | Use `{json: {...}}` |
+| "Unexpected token" | Expression syntax `{{ }}` in code | Remove `{{ }}`, use JavaScript |
+| "Cannot read property X of undefined" | Missing null check | Use optional chaining `?.` |
+| "Cannot read property X of null" | Null value access | Add guard clause or default |
+| "Unmatched expression brackets" | Quote/bracket imbalance | Check string escaping |
+
+---
+
+## Debugging Tips
+
+### 1. Use console.log()
+
+```javascript
+const items = $input.all();
+console.log('Items count:', items.length);
+console.log('First item:', items[0]);
+
+// Check browser console (F12) for output
+```
+
+### 2. Return Intermediate Results
+
+```javascript
+// Debug by returning current state
+const items = $input.all();
+const processed = items.map(item => ({json: item.json}));
+
+// Return to see what you have
+return processed;
+```
+
+### 3. Try-Catch for Troubleshooting
+
+```javascript
+try {
+ // Your code here
+ const result = riskyOperation();
+ return [{json: {result}}];
+} catch (error) {
+ // See what failed
+ return [{
+ json: {
+ error: error.message,
+ stack: error.stack
+ }
+ }];
+}
+```
+
+### 4. Validate Input Structure
+
+```javascript
+const items = $input.all();
+
+// Check what you received
+console.log('Input structure:', JSON.stringify(items[0], null, 2));
+
+// Then process
+```
+
+---
+
+## Summary
+
+**Top 5 Errors to Avoid**:
+1. **Empty code / missing return** (38%) - Always return data
+2. **Expression syntax `{{ }}`** (8%) - Use JavaScript, not expressions
+3. **Wrong return format** (5%) - Always `[{json: {...}}]`
+4. **Unmatched brackets** (6%) - Escape strings properly
+5. **Missing null checks** - Use optional chaining `?.`
+
+**Quick Prevention**:
+- Return `[{json: {...}}]` format
+- Use JavaScript, NOT `{{ }}` expressions
+- Check for null/undefined before accessing
+- Test with empty and invalid data
+- Use browser console for debugging
+
+**See Also**:
+- [SKILL.md](SKILL.md) - Overview and best practices
+- [DATA_ACCESS.md](DATA_ACCESS.md) - Safe data access patterns
+- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Working examples
diff --git a/docs/skills/n8n-automation/javascript-code/README.md b/docs/skills/n8n-automation/javascript-code/README.md
new file mode 100644
index 0000000..df3464f
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/README.md
@@ -0,0 +1,350 @@
+# n8n Code JavaScript
+
+Expert guidance for writing JavaScript code in n8n Code nodes.
+
+---
+
+## Purpose
+
+Teaches how to write effective JavaScript in n8n Code nodes, avoid common errors, and use built-in functions effectively.
+
+---
+
+## Activates On
+
+**Trigger keywords**:
+- "javascript code node"
+- "write javascript in n8n"
+- "code node javascript"
+- "$input syntax"
+- "$json syntax"
+- "$helpers.httpRequest"
+- "DateTime luxon"
+- "code node error"
+- "webhook data code"
+- "return format code node"
+
+**Common scenarios**:
+- Writing JavaScript code in Code nodes
+- Troubleshooting Code node errors
+- Making HTTP requests from code
+- Working with dates and times
+- Accessing webhook data
+- Choosing between All Items and Each Item mode
+
+---
+
+## What You'll Learn
+
+### Quick Start
+- Mode selection (All Items vs Each Item)
+- Data access patterns ($input.all(), $input.first(), $input.item)
+- Correct return format: `[{json: {...}}]`
+- Webhook data structure (.body nesting)
+- Built-in functions overview
+
+### Data Access Mastery
+- $input.all() - Batch operations (most common)
+- $input.first() - Single item operations
+- $input.item - Each Item mode processing
+- $node - Reference other workflow nodes
+- **Critical gotcha**: Webhook data under `.body`
+
+### Common Patterns (Production-Tested)
+1. Multi-source Data Aggregation
+2. Regex Filtering & Pattern Matching
+3. Markdown Parsing & Structured Extraction
+4. JSON Comparison & Validation
+5. CRM Data Transformation
+6. Release Information Processing
+7. Array Transformation with Context
+8. Slack Block Kit Formatting
+9. Top N Filtering & Ranking
+10. String Aggregation & Reporting
+
+### Error Prevention
+Top 5 errors to avoid:
+1. **Empty code / missing return** (38% of failures)
+2. **Expression syntax confusion** (using `{{}}` in code)
+3. **Incorrect return format** (missing array wrapper or json property)
+4. **Unmatched brackets** (string escaping issues)
+5. **Missing null checks** (crashes on undefined)
+
+### Built-in Functions
+- **$helpers.httpRequest()** - Make HTTP requests
+- **DateTime (Luxon)** - Advanced date/time operations
+- **$jmespath()** - Query JSON structures
+- **$getWorkflowStaticData()** - Persistent storage
+- Standard JavaScript globals (Math, JSON, console)
+- Available Node.js modules (crypto, Buffer, URL)
+
+---
+
+## File Structure
+
+```
+n8n-code-javascript/
+├── SKILL.md (500 lines)
+│ Overview, quick start, mode selection, best practices
+│ - Mode selection guide (All Items vs Each Item)
+│ - Data access patterns overview
+│ - Return format requirements
+│ - Critical webhook gotcha
+│ - Error prevention overview
+│ - Quick reference checklist
+│
+├── DATA_ACCESS.md (400 lines)
+│ Complete data access patterns
+│ - $input.all() - Most common (26% usage)
+│ - $input.first() - Very common (25% usage)
+│ - $input.item - Each Item mode (19% usage)
+│ - $node - Reference other nodes
+│ - Webhook data structure (.body nesting)
+│ - Choosing the right pattern
+│ - Common mistakes to avoid
+│
+├── COMMON_PATTERNS.md (600 lines)
+│ 10 production-tested patterns
+│ - Pattern 1: Multi-source Aggregation
+│ - Pattern 2: Regex Filtering
+│ - Pattern 3: Markdown Parsing
+│ - Pattern 4: JSON Comparison
+│ - Pattern 5: CRM Transformation
+│ - Pattern 6: Release Processing
+│ - Pattern 7: Array Transformation
+│ - Pattern 8: Slack Block Kit
+│ - Pattern 9: Top N Filtering
+│ - Pattern 10: String Aggregation
+│ - Pattern selection guide
+│
+├── ERROR_PATTERNS.md (450 lines)
+│ Top 5 errors with solutions
+│ - Error #1: Empty Code / Missing Return (38%)
+│ - Error #2: Expression Syntax Confusion (8%)
+│ - Error #3: Incorrect Return Wrapper (5%)
+│ - Error #4: Unmatched Brackets (6%)
+│ - Error #5: Missing Null Checks
+│ - Error prevention checklist
+│ - Quick error reference
+│ - Debugging tips
+│
+├── BUILTIN_FUNCTIONS.md (450 lines)
+│ Complete built-in function reference
+│ - $helpers.httpRequest() API reference
+│ - DateTime (Luxon) complete guide
+│ - $jmespath() JSON querying
+│ - $getWorkflowStaticData() persistent storage
+│ - Standard JavaScript globals
+│ - Available Node.js modules
+│ - What's NOT available
+│
+└── README.md (this file)
+ Skill metadata and overview
+```
+
+**Total**: ~2,400 lines across 6 files
+
+---
+
+## Coverage
+
+### Mode Selection
+- **Run Once for All Items** - Recommended for 95% of use cases
+- **Run Once for Each Item** - Specialized cases only
+- Decision guide and performance implications
+
+### Data Access
+- Most common patterns with usage statistics
+- Webhook data structure (critical .body gotcha)
+- Safe access patterns with null checks
+- When to use which pattern
+
+### Error Prevention
+- Top 5 errors covering 62%+ of all failures
+- Clear wrong vs right examples
+- Error prevention checklist
+- Debugging tips and console.log usage
+
+### Production Patterns
+- 10 patterns from real workflows
+- Complete working examples
+- Use cases and key techniques
+- Pattern selection guide
+
+### Built-in Functions
+- Complete $helpers.httpRequest() reference
+- DateTime/Luxon operations (formatting, parsing, arithmetic)
+- $jmespath() for JSON queries
+- Persistent storage with $getWorkflowStaticData()
+- Standard JavaScript and Node.js modules
+
+---
+
+## Critical Gotchas Highlighted
+
+### #1: Webhook Data Structure
+**MOST COMMON MISTAKE**: Webhook data is under `.body`
+
+```javascript
+// ❌ WRONG
+const name = $json.name;
+
+// ✅ CORRECT
+const name = $json.body.name;
+```
+
+### #2: Return Format
+**CRITICAL**: Must return array with json property
+
+```javascript
+// ❌ WRONG
+return {json: {result: 'success'}};
+
+// ✅ CORRECT
+return [{json: {result: 'success'}}];
+```
+
+### #3: Expression Syntax
+**Don't use `{{}}` in Code nodes**
+
+```javascript
+// ❌ WRONG
+const value = "{{ $json.field }}";
+
+// ✅ CORRECT
+const value = $json.field;
+```
+
+---
+
+## Integration with Other Skills
+
+### n8n Expression Syntax
+- **Distinction**: Expressions use `{{}}` in OTHER nodes
+- **Code nodes**: Use JavaScript directly (no `{{}}`)
+- **When to use each**: Code vs expressions decision guide
+
+### n8n MCP Tools Expert
+- Find Code node: `search_nodes({query: "code"})`
+- Get configuration: `get_node_essentials("nodes-base.code")`
+- Validate code: `validate_node_operation()`
+
+### n8n Node Configuration
+- Mode selection (All Items vs Each Item)
+- Language selection (JavaScript vs Python)
+- Understanding property dependencies
+
+### n8n Workflow Patterns
+- Code nodes in transformation step
+- Webhook → Code → API pattern
+- Error handling in workflows
+
+### n8n Validation Expert
+- Validate Code node configuration
+- Handle validation errors
+- Auto-fix common issues
+
+---
+
+## When to Use Code Node
+
+**Use Code node when:**
+- ✅ Complex transformations requiring multiple steps
+- ✅ Custom calculations or business logic
+- ✅ Recursive operations
+- ✅ API response parsing with complex structure
+- ✅ Multi-step conditionals
+- ✅ Data aggregation across items
+
+**Consider other nodes when:**
+- ❌ Simple field mapping → Use **Set** node
+- ❌ Basic filtering → Use **Filter** node
+- ❌ Simple conditionals → Use **IF** or **Switch** node
+- ❌ HTTP requests only → Use **HTTP Request** node
+
+**Code node excels at**: Complex logic that would require chaining many simple nodes
+
+---
+
+## Success Metrics
+
+**Before this skill**:
+- Users confused by mode selection
+- Frequent return format errors
+- Expression syntax mistakes
+- Webhook data access failures
+- Missing null check crashes
+
+**After this skill**:
+- Clear mode selection guidance
+- Understanding of return format
+- JavaScript vs expression distinction
+- Correct webhook data access
+- Safe null-handling patterns
+- Production-ready code patterns
+
+---
+
+## Quick Reference
+
+### Essential Rules
+1. Choose "All Items" mode (recommended)
+2. Access data: `$input.all()`, `$input.first()`, `$input.item`
+3. **MUST return**: `[{json: {...}}]` format
+4. **Webhook data**: Under `.body` property
+5. **No `{{}}` syntax**: Use JavaScript directly
+
+### Most Common Patterns
+- Batch processing → $input.all() + map/filter
+- Single item → $input.first()
+- Aggregation → reduce()
+- HTTP requests → $helpers.httpRequest()
+- Date handling → DateTime (Luxon)
+
+### Error Prevention
+- Always return data
+- Check for null/undefined
+- Use try-catch for risky operations
+- Test with empty input
+- Use console.log() for debugging
+
+---
+
+## Related Documentation
+
+- **n8n Code Node Guide**: https://docs.n8n.io/code/code-node/
+- **Built-in Methods Reference**: https://docs.n8n.io/code-examples/methods-variables-reference/
+- **Luxon Documentation**: https://moment.github.io/luxon/
+
+---
+
+## Evaluations
+
+**5 test scenarios** covering:
+1. Webhook body gotcha (most common mistake)
+2. Return format error (missing array wrapper)
+3. HTTP request with $helpers.httpRequest()
+4. Aggregation pattern with $input.all()
+5. Expression syntax confusion (using `{{}}`)
+
+Each evaluation tests skill activation, correct guidance, and reference to appropriate documentation files.
+
+---
+
+## Version History
+
+- **v1.0** (2025-01-20): Initial implementation
+ - SKILL.md with comprehensive overview
+ - DATA_ACCESS.md covering all access patterns
+ - COMMON_PATTERNS.md with 10 production patterns
+ - ERROR_PATTERNS.md covering top 5 errors
+ - BUILTIN_FUNCTIONS.md complete reference
+ - 5 evaluation scenarios
+
+---
+
+## Author
+
+Conceived by Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
+
+Part of the n8n-skills collection.
diff --git a/docs/skills/n8n-automation/javascript-code/SKILL.md b/docs/skills/n8n-automation/javascript-code/SKILL.md
new file mode 100644
index 0000000..449a7b6
--- /dev/null
+++ b/docs/skills/n8n-automation/javascript-code/SKILL.md
@@ -0,0 +1,699 @@
+---
+name: n8n-code-javascript
+description: Write JavaScript code in n8n Code nodes. Use when writing JavaScript in n8n, using $input/$json/$node syntax, making HTTP requests with $helpers, working with dates using DateTime, troubleshooting Code node errors, or choosing between Code node modes.
+---
+
+# JavaScript Code Node
+
+Expert guidance for writing JavaScript code in n8n Code nodes.
+
+---
+
+## Quick Start
+
+```javascript
+// Basic template for Code nodes
+const items = $input.all();
+
+// Process data
+const processed = items.map(item => ({
+ json: {
+ ...item.json,
+ processed: true,
+ timestamp: new Date().toISOString()
+ }
+}));
+
+return processed;
+```
+
+### Essential Rules
+
+1. **Choose "Run Once for All Items" mode** (recommended for most use cases)
+2. **Access data**: `$input.all()`, `$input.first()`, or `$input.item`
+3. **CRITICAL**: Must return `[{json: {...}}]` format
+4. **CRITICAL**: Webhook data is under `$json.body` (not `$json` directly)
+5. **Built-ins available**: $helpers.httpRequest(), DateTime (Luxon), $jmespath()
+
+---
+
+## Mode Selection Guide
+
+The Code node offers two execution modes. Choose based on your use case:
+
+### Run Once for All Items (Recommended - Default)
+
+**Use this mode for:** 95% of use cases
+
+- **How it works**: Code executes **once** regardless of input count
+- **Data access**: `$input.all()` or `items` array
+- **Best for**: Aggregation, filtering, batch processing, transformations, API calls with all data
+- **Performance**: Faster for multiple items (single execution)
+
+```javascript
+// Example: Calculate total from all items
+const allItems = $input.all();
+const total = allItems.reduce((sum, item) => sum + (item.json.amount || 0), 0);
+
+return [{
+ json: {
+ total,
+ count: allItems.length,
+ average: total / allItems.length
+ }
+}];
+```
+
+**When to use:**
+- ✅ Comparing items across the dataset
+- ✅ Calculating totals, averages, or statistics
+- ✅ Sorting or ranking items
+- ✅ Deduplication
+- ✅ Building aggregated reports
+- ✅ Combining data from multiple items
+
+### Run Once for Each Item
+
+**Use this mode for:** Specialized cases only
+
+- **How it works**: Code executes **separately** for each input item
+- **Data access**: `$input.item` or `$item`
+- **Best for**: Item-specific logic, independent operations, per-item validation
+- **Performance**: Slower for large datasets (multiple executions)
+
+```javascript
+// Example: Add processing timestamp to each item
+const item = $input.item;
+
+return [{
+ json: {
+ ...item.json,
+ processed: true,
+ processedAt: new Date().toISOString()
+ }
+}];
+```
+
+**When to use:**
+- ✅ Each item needs independent API call
+- ✅ Per-item validation with different error handling
+- ✅ Item-specific transformations based on item properties
+- ✅ When items must be processed separately for business logic
+
+**Decision Shortcut:**
+- **Need to look at multiple items?** → Use "All Items" mode
+- **Each item completely independent?** → Use "Each Item" mode
+- **Not sure?** → Use "All Items" mode (you can always loop inside)
+
+---
+
+## Data Access Patterns
+
+### Pattern 1: $input.all() - Most Common
+
+**Use when**: Processing arrays, batch operations, aggregations
+
+```javascript
+// Get all items from previous node
+const allItems = $input.all();
+
+// Filter, map, reduce as needed
+const valid = allItems.filter(item => item.json.status === 'active');
+const mapped = valid.map(item => ({
+ json: {
+ id: item.json.id,
+ name: item.json.name
+ }
+}));
+
+return mapped;
+```
+
+### Pattern 2: $input.first() - Very Common
+
+**Use when**: Working with single objects, API responses, first-in-first-out
+
+```javascript
+// Get first item only
+const firstItem = $input.first();
+const data = firstItem.json;
+
+return [{
+ json: {
+ result: processData(data),
+ processedAt: new Date().toISOString()
+ }
+}];
+```
+
+### Pattern 3: $input.item - Each Item Mode Only
+
+**Use when**: In "Run Once for Each Item" mode
+
+```javascript
+// Current item in loop (Each Item mode only)
+const currentItem = $input.item;
+
+return [{
+ json: {
+ ...currentItem.json,
+ itemProcessed: true
+ }
+}];
+```
+
+### Pattern 4: $node - Reference Other Nodes
+
+**Use when**: Need data from specific nodes in workflow
+
+```javascript
+// Get output from specific node
+const webhookData = $node["Webhook"].json;
+const httpData = $node["HTTP Request"].json;
+
+return [{
+ json: {
+ combined: {
+ webhook: webhookData,
+ api: httpData
+ }
+ }
+}];
+```
+
+**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for comprehensive guide
+
+---
+
+## Critical: Webhook Data Structure
+
+**MOST COMMON MISTAKE**: Webhook data is nested under `.body`
+
+```javascript
+// ❌ WRONG - Will return undefined
+const name = $json.name;
+const email = $json.email;
+
+// ✅ CORRECT - Webhook data is under .body
+const name = $json.body.name;
+const email = $json.body.email;
+
+// Or with $input
+const webhookData = $input.first().json.body;
+const name = webhookData.name;
+```
+
+**Why**: Webhook node wraps all request data under `body` property. This includes POST data, query parameters, and JSON payloads.
+
+**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for full webhook structure details
+
+---
+
+## Return Format Requirements
+
+**CRITICAL RULE**: Always return array of objects with `json` property
+
+### Correct Return Formats
+
+```javascript
+// ✅ Single result
+return [{
+ json: {
+ field1: value1,
+ field2: value2
+ }
+}];
+
+// ✅ Multiple results
+return [
+ {json: {id: 1, data: 'first'}},
+ {json: {id: 2, data: 'second'}}
+];
+
+// ✅ Transformed array
+const transformed = $input.all()
+ .filter(item => item.json.valid)
+ .map(item => ({
+ json: {
+ id: item.json.id,
+ processed: true
+ }
+ }));
+return transformed;
+
+// ✅ Empty result (when no data to return)
+return [];
+
+// ✅ Conditional return
+if (shouldProcess) {
+ return [{json: processedData}];
+} else {
+ return [];
+}
+```
+
+### Incorrect Return Formats
+
+```javascript
+// ❌ WRONG: Object without array wrapper
+return {
+ json: {field: value}
+};
+
+// ❌ WRONG: Array without json wrapper
+return [{field: value}];
+
+// ❌ WRONG: Plain string
+return "processed";
+
+// ❌ WRONG: Raw data without mapping
+return $input.all(); // Missing .map()
+
+// ❌ WRONG: Incomplete structure
+return [{data: value}]; // Should be {json: value}
+```
+
+**Why it matters**: Next nodes expect array format. Incorrect format causes workflow execution to fail.
+
+**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) #3 for detailed error solutions
+
+---
+
+## Common Patterns Overview
+
+Based on production workflows, here are the most useful patterns:
+
+### 1. Multi-Source Data Aggregation
+Combine data from multiple APIs, webhooks, or nodes
+
+```javascript
+const allItems = $input.all();
+const results = [];
+
+for (const item of allItems) {
+ const sourceName = item.json.name || 'Unknown';
+ // Parse source-specific structure
+ if (sourceName === 'API1' && item.json.data) {
+ results.push({
+ json: {
+ title: item.json.data.title,
+ source: 'API1'
+ }
+ });
+ }
+}
+
+return results;
+```
+
+### 2. Filtering with Regex
+Extract patterns, mentions, or keywords from text
+
+```javascript
+const pattern = /\b([A-Z]{2,5})\b/g;
+const matches = {};
+
+for (const item of $input.all()) {
+ const text = item.json.text;
+ const found = text.match(pattern);
+
+ if (found) {
+ found.forEach(match => {
+ matches[match] = (matches[match] || 0) + 1;
+ });
+ }
+}
+
+return [{json: {matches}}];
+```
+
+### 3. Data Transformation & Enrichment
+Map fields, normalize formats, add computed fields
+
+```javascript
+const items = $input.all();
+
+return items.map(item => {
+ const data = item.json;
+ const nameParts = data.name.split(' ');
+
+ return {
+ json: {
+ first_name: nameParts[0],
+ last_name: nameParts.slice(1).join(' '),
+ email: data.email,
+ created_at: new Date().toISOString()
+ }
+ };
+});
+```
+
+### 4. Top N Filtering & Ranking
+Sort and limit results
+
+```javascript
+const items = $input.all();
+
+const topItems = items
+ .sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
+ .slice(0, 10);
+
+return topItems.map(item => ({json: item.json}));
+```
+
+### 5. Aggregation & Reporting
+Sum, count, group data
+
+```javascript
+const items = $input.all();
+const total = items.reduce((sum, item) => sum + (item.json.amount || 0), 0);
+
+return [{
+ json: {
+ total,
+ count: items.length,
+ average: total / items.length,
+ timestamp: new Date().toISOString()
+ }
+}];
+```
+
+**See**: [COMMON_PATTERNS.md](COMMON_PATTERNS.md) for 10 detailed production patterns
+
+---
+
+## Error Prevention - Top 5 Mistakes
+
+### #1: Empty Code or Missing Return (Most Common)
+
+```javascript
+// ❌ WRONG: No return statement
+const items = $input.all();
+// ... processing code ...
+// Forgot to return!
+
+// ✅ CORRECT: Always return data
+const items = $input.all();
+// ... processing ...
+return items.map(item => ({json: item.json}));
+```
+
+### #2: Expression Syntax Confusion
+
+```javascript
+// ❌ WRONG: Using n8n expression syntax in code
+const value = "{{ $json.field }}";
+
+// ✅ CORRECT: Use JavaScript template literals
+const value = `${$json.field}`;
+
+// ✅ CORRECT: Direct access
+const value = $input.first().json.field;
+```
+
+### #3: Incorrect Return Wrapper
+
+```javascript
+// ❌ WRONG: Returning object instead of array
+return {json: {result: 'success'}};
+
+// ✅ CORRECT: Array wrapper required
+return [{json: {result: 'success'}}];
+```
+
+### #4: Missing Null Checks
+
+```javascript
+// ❌ WRONG: Crashes if field doesn't exist
+const value = item.json.user.email;
+
+// ✅ CORRECT: Safe access with optional chaining
+const value = item.json?.user?.email || 'no-email@example.com';
+
+// ✅ CORRECT: Guard clause
+if (!item.json.user) {
+ return [];
+}
+const value = item.json.user.email;
+```
+
+### #5: Webhook Body Nesting
+
+```javascript
+// ❌ WRONG: Direct access to webhook data
+const email = $json.email;
+
+// ✅ CORRECT: Webhook data under .body
+const email = $json.body.email;
+```
+
+**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) for comprehensive error guide
+
+---
+
+## Built-in Functions & Helpers
+
+### $helpers.httpRequest()
+
+Make HTTP requests from within code:
+
+```javascript
+const response = await $helpers.httpRequest({
+ method: 'GET',
+ url: 'https://api.example.com/data',
+ headers: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/json'
+ }
+});
+
+return [{json: {data: response}}];
+```
+
+### DateTime (Luxon)
+
+Date and time operations:
+
+```javascript
+// Current time
+const now = DateTime.now();
+
+// Format dates
+const formatted = now.toFormat('yyyy-MM-dd');
+const iso = now.toISO();
+
+// Date arithmetic
+const tomorrow = now.plus({days: 1});
+const lastWeek = now.minus({weeks: 1});
+
+return [{
+ json: {
+ today: formatted,
+ tomorrow: tomorrow.toFormat('yyyy-MM-dd')
+ }
+}];
+```
+
+### $jmespath()
+
+Query JSON structures:
+
+```javascript
+const data = $input.first().json;
+
+// Filter array
+const adults = $jmespath(data, 'users[?age >= `18`]');
+
+// Extract fields
+const names = $jmespath(data, 'users[*].name');
+
+return [{json: {adults, names}}];
+```
+
+**See**: [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) for complete reference
+
+---
+
+## Best Practices
+
+### 1. Always Validate Input Data
+
+```javascript
+const items = $input.all();
+
+// Check if data exists
+if (!items || items.length === 0) {
+ return [];
+}
+
+// Validate structure
+if (!items[0].json) {
+ return [{json: {error: 'Invalid input format'}}];
+}
+
+// Continue processing...
+```
+
+### 2. Use Try-Catch for Error Handling
+
+```javascript
+try {
+ const response = await $helpers.httpRequest({
+ url: 'https://api.example.com/data'
+ });
+
+ return [{json: {success: true, data: response}}];
+} catch (error) {
+ return [{
+ json: {
+ success: false,
+ error: error.message
+ }
+ }];
+}
+```
+
+### 3. Prefer Array Methods Over Loops
+
+```javascript
+// ✅ GOOD: Functional approach
+const processed = $input.all()
+ .filter(item => item.json.valid)
+ .map(item => ({json: {id: item.json.id}}));
+
+// ❌ SLOWER: Manual loop
+const processed = [];
+for (const item of $input.all()) {
+ if (item.json.valid) {
+ processed.push({json: {id: item.json.id}});
+ }
+}
+```
+
+### 4. Filter Early, Process Late
+
+```javascript
+// ✅ GOOD: Filter first to reduce processing
+const processed = $input.all()
+ .filter(item => item.json.status === 'active') // Reduce dataset first
+ .map(item => expensiveTransformation(item)); // Then transform
+
+// ❌ WASTEFUL: Transform everything, then filter
+const processed = $input.all()
+ .map(item => expensiveTransformation(item)) // Wastes CPU
+ .filter(item => item.json.status === 'active');
+```
+
+### 5. Use Descriptive Variable Names
+
+```javascript
+// ✅ GOOD: Clear intent
+const activeUsers = $input.all().filter(item => item.json.active);
+const totalRevenue = activeUsers.reduce((sum, user) => sum + user.json.revenue, 0);
+
+// ❌ BAD: Unclear purpose
+const a = $input.all().filter(item => item.json.active);
+const t = a.reduce((s, u) => s + u.json.revenue, 0);
+```
+
+### 6. Debug with console.log()
+
+```javascript
+// Debug statements appear in browser console
+const items = $input.all();
+console.log(`Processing ${items.length} items`);
+
+for (const item of items) {
+ console.log('Item data:', item.json);
+ // Process...
+}
+
+return result;
+```
+
+---
+
+## When to Use Code Node
+
+Use Code node when:
+- ✅ Complex transformations requiring multiple steps
+- ✅ Custom calculations or business logic
+- ✅ Recursive operations
+- ✅ API response parsing with complex structure
+- ✅ Multi-step conditionals
+- ✅ Data aggregation across items
+
+Consider other nodes when:
+- ❌ Simple field mapping → Use **Set** node
+- ❌ Basic filtering → Use **Filter** node
+- ❌ Simple conditionals → Use **IF** or **Switch** node
+- ❌ HTTP requests only → Use **HTTP Request** node
+
+**Code node excels at**: Complex logic that would require chaining many simple nodes
+
+---
+
+## Integration with Other Skills
+
+### Works With:
+
+**n8n Expression Syntax**:
+- Expressions use `{{ }}` syntax in other nodes
+- Code nodes use JavaScript directly (no `{{ }}`)
+- When to use expressions vs code
+
+**n8n MCP Tools Expert**:
+- How to find Code node: `search_nodes({query: "code"})`
+- Get configuration help: `get_node_essentials("nodes-base.code")`
+- Validate code: `validate_node_operation()`
+
+**n8n Node Configuration**:
+- Mode selection (All Items vs Each Item)
+- Language selection (JavaScript vs Python)
+- Understanding property dependencies
+
+**n8n Workflow Patterns**:
+- Code nodes in transformation step
+- Webhook → Code → API pattern
+- Error handling in workflows
+
+**n8n Validation Expert**:
+- Validate Code node configuration
+- Handle validation errors
+- Auto-fix common issues
+
+---
+
+## Quick Reference Checklist
+
+Before deploying Code nodes, verify:
+
+- [ ] **Code is not empty** - Must have meaningful logic
+- [ ] **Return statement exists** - Must return array of objects
+- [ ] **Proper return format** - Each item: `{json: {...}}`
+- [ ] **Data access correct** - Using `$input.all()`, `$input.first()`, or `$input.item`
+- [ ] **No n8n expressions** - Use JavaScript template literals: `` `${value}` ``
+- [ ] **Error handling** - Guard clauses for null/undefined inputs
+- [ ] **Webhook data** - Access via `.body` if from webhook
+- [ ] **Mode selection** - "All Items" for most cases
+- [ ] **Performance** - Prefer map/filter over manual loops
+- [ ] **Output consistent** - All code paths return same structure
+
+---
+
+## Additional Resources
+
+### Related Files
+- [DATA_ACCESS.md](DATA_ACCESS.md) - Comprehensive data access patterns
+- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - 10 production-tested patterns
+- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Top 5 errors and solutions
+- [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) - Complete built-in reference
+
+### n8n Documentation
+- Code Node Guide: https://docs.n8n.io/code/code-node/
+- Built-in Methods: https://docs.n8n.io/code-examples/methods-variables-reference/
+- Luxon Documentation: https://moment.github.io/luxon/
+
+---
+
+**Ready to write JavaScript in n8n Code nodes!** Start with simple transformations, use the error patterns guide to avoid common mistakes, and reference the pattern library for production-ready examples.
diff --git a/docs/skills/n8n-automation/node-configuration/DEPENDENCIES.md b/docs/skills/n8n-automation/node-configuration/DEPENDENCIES.md
new file mode 100644
index 0000000..ad48183
--- /dev/null
+++ b/docs/skills/n8n-automation/node-configuration/DEPENDENCIES.md
@@ -0,0 +1,800 @@
+# Property Dependencies Guide
+
+Deep dive into n8n property dependencies and displayOptions mechanism.
+
+---
+
+## What Are Property Dependencies?
+
+**Definition**: Rules that control when fields are visible or required based on other field values.
+
+**Mechanism**: `displayOptions` in node schema
+
+**Purpose**:
+- Show relevant fields only
+- Hide irrelevant fields
+- Simplify configuration UX
+- Prevent invalid configurations
+
+---
+
+## displayOptions Structure
+
+### Basic Format
+
+```javascript
+{
+ "name": "fieldName",
+ "type": "string",
+ "displayOptions": {
+ "show": {
+ "otherField": ["value1", "value2"]
+ }
+ }
+}
+```
+
+**Translation**: Show `fieldName` when `otherField` equals "value1" OR "value2"
+
+### Show vs Hide
+
+#### show (Most Common)
+
+**Show field when condition matches**:
+```javascript
+{
+ "name": "body",
+ "displayOptions": {
+ "show": {
+ "sendBody": [true]
+ }
+ }
+}
+```
+
+**Meaning**: Show `body` when `sendBody = true`
+
+#### hide (Less Common)
+
+**Hide field when condition matches**:
+```javascript
+{
+ "name": "advanced",
+ "displayOptions": {
+ "hide": {
+ "simpleMode": [true]
+ }
+ }
+}
+```
+
+**Meaning**: Hide `advanced` when `simpleMode = true`
+
+### Multiple Conditions (AND Logic)
+
+```javascript
+{
+ "name": "body",
+ "displayOptions": {
+ "show": {
+ "sendBody": [true],
+ "method": ["POST", "PUT", "PATCH"]
+ }
+ }
+}
+```
+
+**Meaning**: Show `body` when:
+- `sendBody = true` AND
+- `method IN (POST, PUT, PATCH)`
+
+**All conditions must match** (AND logic)
+
+### Multiple Values (OR Logic)
+
+```javascript
+{
+ "name": "someField",
+ "displayOptions": {
+ "show": {
+ "method": ["POST", "PUT", "PATCH"]
+ }
+ }
+}
+```
+
+**Meaning**: Show `someField` when:
+- `method = POST` OR
+- `method = PUT` OR
+- `method = PATCH`
+
+**Any value matches** (OR logic)
+
+---
+
+## Common Dependency Patterns
+
+### Pattern 1: Boolean Toggle
+
+**Use case**: Optional feature flag
+
+**Example**: HTTP Request sendBody
+```javascript
+// Field: sendBody (boolean)
+{
+ "name": "sendBody",
+ "type": "boolean",
+ "default": false
+}
+
+// Field: body (depends on sendBody)
+{
+ "name": "body",
+ "displayOptions": {
+ "show": {
+ "sendBody": [true]
+ }
+ }
+}
+```
+
+**Flow**:
+1. User sees sendBody checkbox
+2. When checked → body field appears
+3. When unchecked → body field hides
+
+### Pattern 2: Resource/Operation Cascade
+
+**Use case**: Different operations show different fields
+
+**Example**: Slack message operations
+```javascript
+// Operation: post
+{
+ "name": "channel",
+ "displayOptions": {
+ "show": {
+ "resource": ["message"],
+ "operation": ["post"]
+ }
+ }
+}
+
+// Operation: update
+{
+ "name": "messageId",
+ "displayOptions": {
+ "show": {
+ "resource": ["message"],
+ "operation": ["update"]
+ }
+ }
+}
+```
+
+**Flow**:
+1. User selects resource="message"
+2. User selects operation="post" → sees channel
+3. User changes to operation="update" → channel hides, messageId shows
+
+### Pattern 3: Type-Specific Configuration
+
+**Use case**: Different types need different fields
+
+**Example**: IF node conditions
+```javascript
+// String operations
+{
+ "name": "value2",
+ "displayOptions": {
+ "show": {
+ "conditions.string.0.operation": ["equals", "notEquals", "contains"]
+ }
+ }
+}
+
+// Unary operations (isEmpty) don't show value2
+{
+ "displayOptions": {
+ "hide": {
+ "conditions.string.0.operation": ["isEmpty", "isNotEmpty"]
+ }
+ }
+}
+```
+
+### Pattern 4: Method-Specific Fields
+
+**Use case**: HTTP methods have different options
+
+**Example**: HTTP Request
+```javascript
+// Query parameters (all methods can have)
+{
+ "name": "queryParameters",
+ "displayOptions": {
+ "show": {
+ "sendQuery": [true]
+ }
+ }
+}
+
+// Body (only certain methods)
+{
+ "name": "body",
+ "displayOptions": {
+ "show": {
+ "sendBody": [true],
+ "method": ["POST", "PUT", "PATCH", "DELETE"]
+ }
+ }
+}
+```
+
+---
+
+## Using get_property_dependencies
+
+### Basic Usage
+
+```javascript
+const deps = get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+```
+
+### Example Response
+
+```javascript
+{
+ "nodeType": "n8n-nodes-base.httpRequest",
+ "dependencies": {
+ "body": {
+ "shows_when": {
+ "sendBody": [true],
+ "method": ["POST", "PUT", "PATCH", "DELETE"]
+ },
+ "required_when_shown": true
+ },
+ "queryParameters": {
+ "shows_when": {
+ "sendQuery": [true]
+ },
+ "required_when_shown": false
+ },
+ "headerParameters": {
+ "shows_when": {
+ "sendHeaders": [true]
+ },
+ "required_when_shown": false
+ }
+ }
+}
+```
+
+### When to Use
+
+**✅ Use when**:
+- Validation fails with "missing field" but you don't see that field
+- A field appears/disappears unexpectedly
+- You need to understand what controls field visibility
+- Building dynamic configuration tools
+
+**❌ Don't use when**:
+- Simple configuration (use get_node_essentials)
+- Just starting configuration
+- Field requirements are obvious
+
+---
+
+## Complex Dependency Examples
+
+### Example 1: HTTP Request Complete Flow
+
+**Scenario**: Configuring POST with JSON body
+
+**Step 1**: Set method
+```javascript
+{
+ "method": "POST"
+ // → sendBody becomes visible
+}
+```
+
+**Step 2**: Enable body
+```javascript
+{
+ "method": "POST",
+ "sendBody": true
+ // → body field becomes visible AND required
+}
+```
+
+**Step 3**: Configure body
+```javascript
+{
+ "method": "POST",
+ "sendBody": true,
+ "body": {
+ "contentType": "json"
+ // → content field becomes visible AND required
+ }
+}
+```
+
+**Step 4**: Add content
+```javascript
+{
+ "method": "POST",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "name": "John",
+ "email": "john@example.com"
+ }
+ }
+}
+// ✅ Valid!
+```
+
+**Dependency chain**:
+```
+method=POST
+ → sendBody visible
+ → sendBody=true
+ → body visible + required
+ → body.contentType=json
+ → body.content visible + required
+```
+
+### Example 2: IF Node Operator Dependencies
+
+**Scenario**: String comparison with different operators
+
+**Binary operator** (equals):
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "operation": "equals"
+ // → value1 required
+ // → value2 required
+ // → singleValue should NOT be set
+ }
+ ]
+ }
+}
+```
+
+**Unary operator** (isEmpty):
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "operation": "isEmpty"
+ // → value1 required
+ // → value2 should NOT be set
+ // → singleValue should be true (auto-added)
+ }
+ ]
+ }
+}
+```
+
+**Dependency table**:
+
+| Operator | value1 | value2 | singleValue |
+|---|---|---|---|
+| equals | Required | Required | false |
+| notEquals | Required | Required | false |
+| contains | Required | Required | false |
+| isEmpty | Required | Hidden | true |
+| isNotEmpty | Required | Hidden | true |
+
+### Example 3: Slack Operation Matrix
+
+**Scenario**: Different Slack operations show different fields
+
+```javascript
+// post message
+{
+ "resource": "message",
+ "operation": "post"
+ // Shows: channel (required), text (required), attachments, blocks
+}
+
+// update message
+{
+ "resource": "message",
+ "operation": "update"
+ // Shows: messageId (required), text (required), channel (optional)
+}
+
+// delete message
+{
+ "resource": "message",
+ "operation": "delete"
+ // Shows: messageId (required), channel (required)
+ // Hides: text, attachments, blocks
+}
+
+// get message
+{
+ "resource": "message",
+ "operation": "get"
+ // Shows: messageId (required), channel (required)
+ // Hides: text, attachments, blocks
+}
+```
+
+**Field visibility matrix**:
+
+| Field | post | update | delete | get |
+|---|---|---|---|---|
+| channel | Required | Optional | Required | Required |
+| text | Required | Required | Hidden | Hidden |
+| messageId | Hidden | Required | Required | Required |
+| attachments | Optional | Optional | Hidden | Hidden |
+| blocks | Optional | Optional | Hidden | Hidden |
+
+---
+
+## Nested Dependencies
+
+### What Are They?
+
+**Definition**: Dependencies within object properties
+
+**Example**: HTTP Request body.contentType controls body.content structure
+
+```javascript
+{
+ "body": {
+ "contentType": "json",
+ // → content expects JSON object
+ "content": {
+ "key": "value"
+ }
+ }
+}
+
+{
+ "body": {
+ "contentType": "form-data",
+ // → content expects form fields array
+ "content": [
+ {
+ "name": "field1",
+ "value": "value1"
+ }
+ ]
+ }
+}
+```
+
+### How to Handle
+
+**Strategy**: Configure parent first, then children
+
+```javascript
+// Step 1: Parent
+{
+ "body": {
+ "contentType": "json" // Set parent first
+ }
+}
+
+// Step 2: Children (structure determined by parent)
+{
+ "body": {
+ "contentType": "json",
+ "content": { // JSON object format
+ "key": "value"
+ }
+ }
+}
+```
+
+---
+
+## Auto-Sanitization and Dependencies
+
+### What Auto-Sanitization Fixes
+
+**Operator structure issues** (IF/Switch nodes):
+
+**Example**: singleValue property
+```javascript
+// You configure (missing singleValue)
+{
+ "type": "boolean",
+ "operation": "isEmpty"
+ // Missing singleValue
+}
+
+// Auto-sanitization adds it
+{
+ "type": "boolean",
+ "operation": "isEmpty",
+ "singleValue": true // ✅ Added automatically
+}
+```
+
+### What It Doesn't Fix
+
+**Missing required fields**:
+```javascript
+// You configure (missing channel)
+{
+ "resource": "message",
+ "operation": "post",
+ "text": "Hello"
+ // Missing required field: channel
+}
+
+// Auto-sanitization does NOT add
+// You must add it yourself
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general", // ← You must add
+ "text": "Hello"
+}
+```
+
+---
+
+## Troubleshooting Dependencies
+
+### Problem 1: "Field X is required but not visible"
+
+**Error**:
+```json
+{
+ "type": "missing_required",
+ "property": "body",
+ "message": "body is required"
+}
+```
+
+**But you don't see body field in configuration!**
+
+**Solution**:
+```javascript
+// Check dependencies
+const deps = get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+
+// Find that body shows when sendBody=true
+// Add sendBody
+{
+ "method": "POST",
+ "sendBody": true, // ← Now body appears!
+ "body": {...}
+}
+```
+
+### Problem 2: "Field disappears when I change operation"
+
+**Scenario**:
+```javascript
+// Working configuration
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general",
+ "text": "Hello"
+}
+
+// Change operation
+{
+ "resource": "message",
+ "operation": "update", // Changed
+ "channel": "#general", // Still here
+ "text": "Updated" // Still here
+ // Missing: messageId (required for update!)
+}
+```
+
+**Validation error**: "messageId is required"
+
+**Why**: Different operation = different required fields
+
+**Solution**:
+```javascript
+// Check essentials for new operation
+get_node_essentials({
+ nodeType: "nodes-base.slack"
+});
+
+// Configure for update operation
+{
+ "resource": "message",
+ "operation": "update",
+ "messageId": "1234567890", // Required for update
+ "text": "Updated",
+ "channel": "#general" // Optional for update
+}
+```
+
+### Problem 3: "Validation passes but field doesn't save"
+
+**Scenario**: Field hidden by dependencies after validation
+
+**Example**:
+```javascript
+// Configure
+{
+ "method": "GET",
+ "sendBody": true, // ❌ GET doesn't support body
+ "body": {...} // This will be stripped
+}
+
+// After save
+{
+ "method": "GET"
+ // body removed because method=GET hides it
+}
+```
+
+**Solution**: Respect dependencies from the start
+
+```javascript
+// Correct approach
+get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+
+// See that body only shows for POST/PUT/PATCH/DELETE
+// Use correct method
+{
+ "method": "POST",
+ "sendBody": true,
+ "body": {...}
+}
+```
+
+---
+
+## Advanced Patterns
+
+### Pattern 1: Conditional Required with Fallback
+
+**Example**: Channel can be string OR expression
+
+```javascript
+// Option 1: String
+{
+ "channel": "#general"
+}
+
+// Option 2: Expression
+{
+ "channel": "={{$json.channelName}}"
+}
+
+// Validation accepts both
+```
+
+### Pattern 2: Mutually Exclusive Fields
+
+**Example**: Use either ID or name, not both
+
+```javascript
+// Use messageId
+{
+ "messageId": "1234567890"
+ // name not needed
+}
+
+// OR use messageName
+{
+ "messageName": "thread-name"
+ // messageId not needed
+}
+
+// Dependencies ensure only one is required
+```
+
+### Pattern 3: Progressive Complexity
+
+**Example**: Simple mode vs advanced mode
+
+```javascript
+// Simple mode
+{
+ "mode": "simple",
+ "text": "{{$json.message}}"
+ // Advanced fields hidden
+}
+
+// Advanced mode
+{
+ "mode": "advanced",
+ "attachments": [...],
+ "blocks": [...],
+ "metadata": {...}
+ // Simple field hidden, advanced fields shown
+}
+```
+
+---
+
+## Best Practices
+
+### ✅ Do
+
+1. **Check dependencies when stuck**
+ ```javascript
+ get_property_dependencies({nodeType: "..."});
+ ```
+
+2. **Configure parent properties first**
+ ```javascript
+ // First: method, resource, operation
+ // Then: dependent fields
+ ```
+
+3. **Validate after changing operation**
+ ```javascript
+ // Operation changed → requirements changed
+ validate_node_operation({...});
+ ```
+
+4. **Read validation errors for dependency hints**
+ ```
+ Error: "body required when sendBody=true"
+ → Hint: Set sendBody=true to enable body
+ ```
+
+### ❌ Don't
+
+1. **Don't ignore dependency errors**
+ ```javascript
+ // Error: "body not visible" → Check displayOptions
+ ```
+
+2. **Don't hardcode all possible fields**
+ ```javascript
+ // Bad: Adding fields that will be hidden
+ ```
+
+3. **Don't assume operations are identical**
+ ```javascript
+ // Each operation has unique requirements
+ ```
+
+---
+
+## Summary
+
+**Key Concepts**:
+- `displayOptions` control field visibility
+- `show` = field appears when conditions match
+- `hide` = field disappears when conditions match
+- Multiple conditions = AND logic
+- Multiple values = OR logic
+
+**Common Patterns**:
+1. Boolean toggle (sendBody → body)
+2. Resource/operation cascade (different operations → different fields)
+3. Type-specific config (string vs boolean conditions)
+4. Method-specific fields (GET vs POST)
+
+**Troubleshooting**:
+- Field required but not visible → Check dependencies
+- Field disappears after change → Operation changed requirements
+- Field doesn't save → Hidden by dependencies
+
+**Tools**:
+- `get_property_dependencies` - See dependency rules
+- `get_node_essentials` - See operation requirements
+- Validation errors - Hints about dependencies
+
+**Related Files**:
+- **[SKILL.md](SKILL.md)** - Main configuration guide
+- **[OPERATION_PATTERNS.md](OPERATION_PATTERNS.md)** - Common patterns by node type
diff --git a/docs/skills/n8n-automation/node-configuration/OPERATION_PATTERNS.md b/docs/skills/n8n-automation/node-configuration/OPERATION_PATTERNS.md
new file mode 100644
index 0000000..c647800
--- /dev/null
+++ b/docs/skills/n8n-automation/node-configuration/OPERATION_PATTERNS.md
@@ -0,0 +1,913 @@
+# Operation Patterns Guide
+
+Common node configuration patterns organized by node type and operation.
+
+---
+
+## Overview
+
+**Purpose**: Quick reference for common node configurations
+
+**Coverage**: Top 20 most-used nodes from 525 available
+
+**Pattern format**:
+- Minimal valid configuration
+- Common options
+- Real-world examples
+- Gotchas and tips
+
+---
+
+## HTTP & API Nodes
+
+### HTTP Request (nodes-base.httpRequest)
+
+Most versatile node for HTTP operations
+
+#### GET Request
+
+**Minimal**:
+```javascript
+{
+ "method": "GET",
+ "url": "https://api.example.com/users",
+ "authentication": "none"
+}
+```
+
+**With query parameters**:
+```javascript
+{
+ "method": "GET",
+ "url": "https://api.example.com/users",
+ "authentication": "none",
+ "sendQuery": true,
+ "queryParameters": {
+ "parameters": [
+ {
+ "name": "limit",
+ "value": "100"
+ },
+ {
+ "name": "offset",
+ "value": "={{$json.offset}}"
+ }
+ ]
+ }
+}
+```
+
+**With authentication**:
+```javascript
+{
+ "method": "GET",
+ "url": "https://api.example.com/users",
+ "authentication": "predefinedCredentialType",
+ "nodeCredentialType": "httpHeaderAuth"
+}
+```
+
+#### POST with JSON
+
+**Minimal**:
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/users",
+ "authentication": "none",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "name": "John Doe",
+ "email": "john@example.com"
+ }
+ }
+}
+```
+
+**With expressions**:
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/users",
+ "authentication": "none",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "name": "={{$json.name}}",
+ "email": "={{$json.email}}",
+ "metadata": {
+ "source": "n8n",
+ "timestamp": "={{$now.toISO()}}"
+ }
+ }
+ }
+}
+```
+
+**Gotcha**: Remember `sendBody: true` for POST/PUT/PATCH!
+
+#### PUT/PATCH Request
+
+**Pattern**: Same as POST, but method changes
+```javascript
+{
+ "method": "PUT", // or "PATCH"
+ "url": "https://api.example.com/users/123",
+ "authentication": "none",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "name": "Updated Name"
+ }
+ }
+}
+```
+
+#### DELETE Request
+
+**Minimal** (no body):
+```javascript
+{
+ "method": "DELETE",
+ "url": "https://api.example.com/users/123",
+ "authentication": "none"
+}
+```
+
+**With body** (some APIs allow):
+```javascript
+{
+ "method": "DELETE",
+ "url": "https://api.example.com/users",
+ "authentication": "none",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "ids": ["123", "456"]
+ }
+ }
+}
+```
+
+---
+
+### Webhook (nodes-base.webhook)
+
+Most common trigger - 813 searches!
+
+#### Basic Webhook
+
+**Minimal**:
+```javascript
+{
+ "path": "my-webhook",
+ "httpMethod": "POST",
+ "responseMode": "onReceived"
+}
+```
+
+**Gotcha**: Webhook data is under `$json.body`, not `$json`!
+
+```javascript
+// ❌ Wrong
+{
+ "text": "={{$json.email}}"
+}
+
+// ✅ Correct
+{
+ "text": "={{$json.body.email}}"
+}
+```
+
+#### Webhook with Authentication
+
+**Header auth**:
+```javascript
+{
+ "path": "secure-webhook",
+ "httpMethod": "POST",
+ "responseMode": "onReceived",
+ "authentication": "headerAuth",
+ "options": {
+ "responseCode": 200,
+ "responseData": "{\n \"success\": true\n}"
+ }
+}
+```
+
+#### Webhook Returning Data
+
+**Custom response**:
+```javascript
+{
+ "path": "my-webhook",
+ "httpMethod": "POST",
+ "responseMode": "lastNode", // Return data from last node
+ "options": {
+ "responseCode": 201,
+ "responseHeaders": {
+ "entries": [
+ {
+ "name": "Content-Type",
+ "value": "application/json"
+ }
+ ]
+ }
+ }
+}
+```
+
+---
+
+## Communication Nodes
+
+### Slack (nodes-base.slack)
+
+Popular choice for AI agent workflows
+
+#### Post Message
+
+**Minimal**:
+```javascript
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general",
+ "text": "Hello from n8n!"
+}
+```
+
+**With dynamic content**:
+```javascript
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "={{$json.channel}}",
+ "text": "New user: {{$json.name}} ({{$json.email}})"
+}
+```
+
+**With attachments**:
+```javascript
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#alerts",
+ "text": "Error Alert",
+ "attachments": [
+ {
+ "color": "#ff0000",
+ "fields": [
+ {
+ "title": "Error Type",
+ "value": "={{$json.errorType}}"
+ },
+ {
+ "title": "Timestamp",
+ "value": "={{$now.toLocaleString()}}"
+ }
+ ]
+ }
+ ]
+}
+```
+
+**Gotcha**: Channel must start with `#` for public channels or be a channel ID!
+
+#### Update Message
+
+**Minimal**:
+```javascript
+{
+ "resource": "message",
+ "operation": "update",
+ "messageId": "1234567890.123456", // From previous message post
+ "text": "Updated message content"
+}
+```
+
+**Note**: `messageId` required, `channel` optional (can be inferred)
+
+#### Create Channel
+
+**Minimal**:
+```javascript
+{
+ "resource": "channel",
+ "operation": "create",
+ "name": "new-project-channel", // Lowercase, no spaces
+ "isPrivate": false
+}
+```
+
+**Gotcha**: Channel name must be lowercase, no spaces, 1-80 chars!
+
+---
+
+### Gmail (nodes-base.gmail)
+
+Popular for email automation
+
+#### Send Email
+
+**Minimal**:
+```javascript
+{
+ "resource": "message",
+ "operation": "send",
+ "to": "user@example.com",
+ "subject": "Hello from n8n",
+ "message": "This is the email body"
+}
+```
+
+**With dynamic content**:
+```javascript
+{
+ "resource": "message",
+ "operation": "send",
+ "to": "={{$json.email}}",
+ "subject": "Order Confirmation #{{$json.orderId}}",
+ "message": "Dear {{$json.name}},\n\nYour order has been confirmed.\n\nThank you!",
+ "options": {
+ "ccList": "admin@example.com",
+ "replyTo": "support@example.com"
+ }
+}
+```
+
+#### Get Email
+
+**Minimal**:
+```javascript
+{
+ "resource": "message",
+ "operation": "getAll",
+ "returnAll": false,
+ "limit": 10
+}
+```
+
+**With filters**:
+```javascript
+{
+ "resource": "message",
+ "operation": "getAll",
+ "returnAll": false,
+ "limit": 50,
+ "filters": {
+ "q": "is:unread from:important@example.com",
+ "labelIds": ["INBOX"]
+ }
+}
+```
+
+---
+
+## Database Nodes
+
+### Postgres (nodes-base.postgres)
+
+Database operations - 456 templates
+
+#### Execute Query
+
+**Minimal** (SELECT):
+```javascript
+{
+ "operation": "executeQuery",
+ "query": "SELECT * FROM users WHERE active = true LIMIT 100"
+}
+```
+
+**With parameters** (SQL injection prevention):
+```javascript
+{
+ "operation": "executeQuery",
+ "query": "SELECT * FROM users WHERE email = $1 AND active = $2",
+ "additionalFields": {
+ "mode": "list",
+ "queryParameters": "user@example.com,true"
+ }
+}
+```
+
+**Gotcha**: ALWAYS use parameterized queries for user input!
+
+```javascript
+// ❌ BAD - SQL injection risk!
+{
+ "query": "SELECT * FROM users WHERE email = '{{$json.email}}'"
+}
+
+// ✅ GOOD - Parameterized
+{
+ "query": "SELECT * FROM users WHERE email = $1",
+ "additionalFields": {
+ "mode": "list",
+ "queryParameters": "={{$json.email}}"
+ }
+}
+```
+
+#### Insert
+
+**Minimal**:
+```javascript
+{
+ "operation": "insert",
+ "table": "users",
+ "columns": "name,email,created_at",
+ "additionalFields": {
+ "mode": "list",
+ "queryParameters": "John Doe,john@example.com,NOW()"
+ }
+}
+```
+
+**With expressions**:
+```javascript
+{
+ "operation": "insert",
+ "table": "users",
+ "columns": "name,email,metadata",
+ "additionalFields": {
+ "mode": "list",
+ "queryParameters": "={{$json.name}},={{$json.email}},{{JSON.stringify($json)}}"
+ }
+}
+```
+
+#### Update
+
+**Minimal**:
+```javascript
+{
+ "operation": "update",
+ "table": "users",
+ "updateKey": "id",
+ "columns": "name,email",
+ "additionalFields": {
+ "mode": "list",
+ "queryParameters": "={{$json.id}},Updated Name,newemail@example.com"
+ }
+}
+```
+
+---
+
+## Data Transformation Nodes
+
+### Set (nodes-base.set)
+
+Most used transformation - 68% of workflows!
+
+#### Set Fixed Values
+
+**Minimal**:
+```javascript
+{
+ "mode": "manual",
+ "duplicateItem": false,
+ "assignments": {
+ "assignments": [
+ {
+ "name": "status",
+ "value": "active",
+ "type": "string"
+ },
+ {
+ "name": "count",
+ "value": 100,
+ "type": "number"
+ }
+ ]
+ }
+}
+```
+
+#### Set from Input Data
+
+**Mapping data**:
+```javascript
+{
+ "mode": "manual",
+ "duplicateItem": false,
+ "assignments": {
+ "assignments": [
+ {
+ "name": "fullName",
+ "value": "={{$json.firstName}} {{$json.lastName}}",
+ "type": "string"
+ },
+ {
+ "name": "email",
+ "value": "={{$json.email.toLowerCase()}}",
+ "type": "string"
+ },
+ {
+ "name": "timestamp",
+ "value": "={{$now.toISO()}}",
+ "type": "string"
+ }
+ ]
+ }
+}
+```
+
+**Gotcha**: Use correct `type` for each field!
+
+```javascript
+// ❌ Wrong type
+{
+ "name": "age",
+ "value": "25", // String
+ "type": "string" // Will be string "25"
+}
+
+// ✅ Correct type
+{
+ "name": "age",
+ "value": 25, // Number
+ "type": "number" // Will be number 25
+}
+```
+
+---
+
+### Code (nodes-base.code)
+
+JavaScript execution - 42% of workflows
+
+#### Simple Transformation
+
+**Minimal**:
+```javascript
+{
+ "mode": "runOnceForAllItems",
+ "jsCode": "return $input.all().map(item => ({\n json: {\n name: item.json.name.toUpperCase(),\n email: item.json.email\n }\n}));"
+}
+```
+
+**Per-item processing**:
+```javascript
+{
+ "mode": "runOnceForEachItem",
+ "jsCode": "// Process each item\nconst data = $input.item.json;\n\nreturn {\n json: {\n fullName: `${data.firstName} ${data.lastName}`,\n email: data.email.toLowerCase(),\n timestamp: new Date().toISOString()\n }\n};"
+}
+```
+
+**Gotcha**: In Code nodes, use `$input.item.json` or `$input.all()`, NOT `{{...}}`!
+
+```javascript
+// ❌ Wrong - expressions don't work in Code nodes
+{
+ "jsCode": "const name = '={{$json.name}}';"
+}
+
+// ✅ Correct - direct access
+{
+ "jsCode": "const name = $input.item.json.name;"
+}
+```
+
+---
+
+## Conditional Nodes
+
+### IF (nodes-base.if)
+
+Conditional logic - 38% of workflows
+
+#### String Comparison
+
+**Equals** (binary):
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "active"
+ }
+ ]
+ }
+}
+```
+
+**Contains** (binary):
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.email}}",
+ "operation": "contains",
+ "value2": "@example.com"
+ }
+ ]
+ }
+}
+```
+
+**isEmpty** (unary):
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.email}}",
+ "operation": "isEmpty"
+ // No value2 - unary operator
+ // singleValue: true added by auto-sanitization
+ }
+ ]
+ }
+}
+```
+
+**Gotcha**: Unary operators (isEmpty, isNotEmpty) don't need value2!
+
+#### Number Comparison
+
+**Greater than**:
+```javascript
+{
+ "conditions": {
+ "number": [
+ {
+ "value1": "={{$json.age}}",
+ "operation": "larger",
+ "value2": 18
+ }
+ ]
+ }
+}
+```
+
+#### Boolean Comparison
+
+**Is true**:
+```javascript
+{
+ "conditions": {
+ "boolean": [
+ {
+ "value1": "={{$json.isActive}}",
+ "operation": "true"
+ // Unary - no value2
+ }
+ ]
+ }
+}
+```
+
+#### Multiple Conditions (AND)
+
+**All must match**:
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "active"
+ }
+ ],
+ "number": [
+ {
+ "value1": "={{$json.age}}",
+ "operation": "larger",
+ "value2": 18
+ }
+ ]
+ },
+ "combineOperation": "all" // AND logic
+}
+```
+
+#### Multiple Conditions (OR)
+
+**Any can match**:
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "active"
+ },
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "pending"
+ }
+ ]
+ },
+ "combineOperation": "any" // OR logic
+}
+```
+
+---
+
+### Switch (nodes-base.switch)
+
+Multi-way routing - 18% of workflows
+
+#### Basic Switch
+
+**Minimal**:
+```javascript
+{
+ "mode": "rules",
+ "rules": {
+ "rules": [
+ {
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "active"
+ }
+ ]
+ }
+ },
+ {
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "pending"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "fallbackOutput": "extra" // Catch-all for non-matching
+}
+```
+
+**Gotcha**: Number of rules must match number of outputs!
+
+---
+
+## AI Nodes
+
+### OpenAI (nodes-langchain.openAi)
+
+AI operations - 234 templates
+
+#### Chat Completion
+
+**Minimal**:
+```javascript
+{
+ "resource": "chat",
+ "operation": "complete",
+ "messages": {
+ "values": [
+ {
+ "role": "user",
+ "content": "={{$json.prompt}}"
+ }
+ ]
+ }
+}
+```
+
+**With system prompt**:
+```javascript
+{
+ "resource": "chat",
+ "operation": "complete",
+ "messages": {
+ "values": [
+ {
+ "role": "system",
+ "content": "You are a helpful assistant specialized in customer support."
+ },
+ {
+ "role": "user",
+ "content": "={{$json.userMessage}}"
+ }
+ ]
+ },
+ "options": {
+ "temperature": 0.7,
+ "maxTokens": 500
+ }
+}
+```
+
+---
+
+## Schedule Nodes
+
+### Schedule Trigger (nodes-base.scheduleTrigger)
+
+Time-based workflows - 28% have schedule triggers
+
+#### Daily at Specific Time
+
+**Minimal**:
+```javascript
+{
+ "rule": {
+ "interval": [
+ {
+ "field": "hours",
+ "hoursInterval": 24
+ }
+ ],
+ "hour": 9,
+ "minute": 0,
+ "timezone": "America/New_York"
+ }
+}
+```
+
+**Gotcha**: Always set timezone explicitly!
+
+```javascript
+// ❌ Bad - uses server timezone
+{
+ "rule": {
+ "interval": [...]
+ }
+}
+
+// ✅ Good - explicit timezone
+{
+ "rule": {
+ "interval": [...],
+ "timezone": "America/New_York"
+ }
+}
+```
+
+#### Every N Minutes
+
+**Minimal**:
+```javascript
+{
+ "rule": {
+ "interval": [
+ {
+ "field": "minutes",
+ "minutesInterval": 15
+ }
+ ]
+ }
+}
+```
+
+#### Cron Expression
+
+**Advanced scheduling**:
+```javascript
+{
+ "mode": "cron",
+ "cronExpression": "0 */2 * * *", // Every 2 hours
+ "timezone": "America/New_York"
+}
+```
+
+---
+
+## Summary
+
+**Key Patterns by Category**:
+
+| Category | Most Common | Key Gotcha |
+|---|---|---|
+| HTTP/API | GET, POST JSON | Remember sendBody: true |
+| Webhooks | POST receiver | Data under $json.body |
+| Communication | Slack post | Channel format (#name) |
+| Database | SELECT with params | Use parameterized queries |
+| Transform | Set assignments | Correct type per field |
+| Conditional | IF string equals | Unary vs binary operators |
+| AI | OpenAI chat | System + user messages |
+| Schedule | Daily at time | Set timezone explicitly |
+
+**Configuration Approach**:
+1. Use patterns as starting point
+2. Adapt to your use case
+3. Validate configuration
+4. Iterate based on errors
+5. Deploy when valid
+
+**Related Files**:
+- **[SKILL.md](SKILL.md)** - Configuration workflow and philosophy
+- **[DEPENDENCIES.md](DEPENDENCIES.md)** - Property dependency rules
diff --git a/docs/skills/n8n-automation/node-configuration/README.md b/docs/skills/n8n-automation/node-configuration/README.md
new file mode 100644
index 0000000..b3efb5e
--- /dev/null
+++ b/docs/skills/n8n-automation/node-configuration/README.md
@@ -0,0 +1,364 @@
+# n8n Node Configuration
+
+Expert guidance for operation-aware node configuration with property dependencies.
+
+## Overview
+
+**Skill Name**: n8n Node Configuration
+**Priority**: Medium
+**Purpose**: Teach operation-aware configuration with progressive discovery and dependency awareness
+
+## The Problem This Solves
+
+Node configuration patterns:
+
+- get_node_essentials is the primary discovery tool (18s avg from search → essentials)
+- 91.7% success rate with essentials-based configuration
+- 56 seconds average between configuration edits
+
+**Key insight**: Most configurations only need essentials, not full schema!
+
+## What This Skill Teaches
+
+### Core Concepts
+
+1. **Operation-Aware Configuration**
+ - Resource + operation determine required fields
+ - Different operations = different requirements
+ - Always check requirements when changing operation
+
+2. **Property Dependencies**
+ - Fields appear/disappear based on other field values
+ - displayOptions control visibility
+ - Conditional required fields
+ - Understanding dependency chains
+
+3. **Progressive Discovery**
+ - Start with get_node_essentials (91.7% success)
+ - Escalate to get_property_dependencies if needed
+ - Use get_node_info only when necessary
+ - Right tool for right job
+
+4. **Configuration Workflow**
+ - Identify → Discover → Configure → Validate → Iterate
+ - Average 2-3 validation cycles
+ - Read errors for dependency hints
+ - 56 seconds between edits average
+
+5. **Common Patterns**
+ - Resource/operation nodes (Slack, Sheets)
+ - HTTP-based nodes (HTTP Request, Webhook)
+ - Database nodes (Postgres, MySQL)
+ - Conditional logic nodes (IF, Switch)
+
+## File Structure
+
+```
+n8n-node-configuration/
+├── SKILL.md (692 lines)
+│ Main configuration guide
+│ - Configuration philosophy (progressive disclosure)
+│ - Core concepts (operation-aware, dependencies)
+│ - Configuration workflow (8-step process)
+│ - get_node_essentials vs get_node_info
+│ - Property dependencies deep dive
+│ - Common node patterns (4 categories)
+│ - Operation-specific examples
+│ - Conditional requirements
+│ - Anti-patterns and best practices
+│
+├── DEPENDENCIES.md (671 lines)
+│ Property dependencies reference
+│ - displayOptions mechanism
+│ - show vs hide rules
+│ - Multiple conditions (AND logic)
+│ - Multiple values (OR logic)
+│ - 4 common dependency patterns
+│ - Using get_property_dependencies
+│ - Complex dependency examples
+│ - Nested dependencies
+│ - Auto-sanitization interaction
+│ - Troubleshooting guide
+│ - Advanced patterns
+│
+├── OPERATION_PATTERNS.md (783 lines)
+│ Common configurations by node type
+│ - HTTP Request (GET/POST/PUT/DELETE)
+│ - Webhook (basic/auth/response)
+│ - Slack (post/update/create)
+│ - Gmail (send/get)
+│ - Postgres (query/insert/update)
+│ - Set (values/mapping)
+│ - Code (per-item/all-items)
+│ - IF (string/number/boolean)
+│ - Switch (rules/fallback)
+│ - OpenAI (chat completion)
+│ - Schedule (daily/interval/cron)
+│ - Gotchas and tips for each
+│
+└── README.md (this file)
+ Skill metadata and statistics
+```
+
+**Total**: ~2,146 lines across 4 files + 4 evaluations
+
+## Usage Statistics
+
+Configuration metrics:
+
+| Metric | Value | Insight |
+|---|---|---|
+| get_node_essentials | Primary tool | Most popular discovery pattern |
+| Success rate (essentials) | 91.7% | Essentials sufficient for most |
+| Avg time search→essentials | 18 seconds | Fast discovery workflow |
+| Avg time between edits | 56 seconds | Iterative configuration |
+
+## Tool Usage Pattern
+
+**Most common discovery pattern**:
+```
+search_nodes → get_node_essentials (18s average)
+```
+
+**Configuration cycle**:
+```
+get_node_essentials → configure → validate → iterate (56s avg per edit)
+```
+
+## Key Insights
+
+### 1. Progressive Disclosure Works
+
+**91.7% success rate** with get_node_essentials proves most configurations don't need full schema.
+
+**Strategy**:
+1. Start with essentials
+2. Escalate to dependencies if stuck
+3. Use full schema only when necessary
+
+### 2. Operations Determine Requirements
+
+**Same node, different operation = different requirements**
+
+Example: Slack message
+- `operation="post"` → needs channel + text
+- `operation="update"` → needs messageId + text (different!)
+
+### 3. Dependencies Control Visibility
+
+**Fields appear/disappear based on other values**
+
+Example: HTTP Request
+- `method="GET"` → body hidden
+- `method="POST"` + `sendBody=true` → body required
+
+### 4. Configuration is Iterative
+
+**Average 56 seconds between edits** shows configuration is iterative, not one-shot.
+
+**Normal workflow**:
+1. Configure minimal
+2. Validate → error
+3. Add missing field
+4. Validate → error
+5. Adjust value
+6. Validate → valid ✅
+
+### 5. Common Gotchas Exist
+
+**Top 5 gotchas** from patterns:
+1. Webhook data under `$json.body` (not `$json`)
+2. POST needs `sendBody: true`
+3. Slack channel format (`#name`)
+4. SQL parameterized queries (injection prevention)
+5. Timezone must be explicit (schedule nodes)
+
+## Usage Examples
+
+### Example 1: Basic Configuration Flow
+
+```javascript
+// Step 1: Get essentials
+const info = get_node_essentials({
+ nodeType: "nodes-base.slack"
+});
+
+// Step 2: Configure for operation
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general",
+ "text": "Hello!"
+}
+
+// Step 3: Validate
+validate_node_operation({...});
+// ✅ Valid!
+```
+
+### Example 2: Handling Dependencies
+
+```javascript
+// Step 1: Configure HTTP POST
+{
+ "method": "POST",
+ "url": "https://api.example.com/create"
+}
+
+// Step 2: Validate → Error: "sendBody required"
+// Step 3: Check dependencies
+get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+// Shows: body visible when sendBody=true
+
+// Step 4: Fix
+{
+ "method": "POST",
+ "url": "https://api.example.com/create",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {...}
+ }
+}
+// ✅ Valid!
+```
+
+### Example 3: Operation Change
+
+```javascript
+// Initial config (post operation)
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general",
+ "text": "Hello"
+}
+
+// Change operation
+{
+ "resource": "message",
+ "operation": "update", // Changed!
+ // Need to check new requirements
+}
+
+// Get essentials for update operation
+get_node_essentials({nodeType: "nodes-base.slack"});
+// Shows: messageId required, channel optional
+
+// Correct config
+{
+ "resource": "message",
+ "operation": "update",
+ "messageId": "1234567890.123456",
+ "text": "Updated"
+}
+```
+
+## When This Skill Activates
+
+**Trigger phrases**:
+- "how to configure"
+- "what fields are required"
+- "property dependencies"
+- "get_node_essentials vs get_node_info"
+- "operation-specific"
+- "field not visible"
+
+**Common scenarios**:
+- Configuring new nodes
+- Understanding required fields
+- Field appears/disappears unexpectedly
+- Choosing between discovery tools
+- Switching operations
+- Learning common patterns
+
+## Integration with Other Skills
+
+### Works With:
+- **n8n MCP Tools Expert** - How to call discovery tools correctly
+- **n8n Validation Expert** - Interpret missing_required errors
+- **n8n Expression Syntax** - Configure expression fields
+- **n8n Workflow Patterns** - Apply patterns with proper node config
+
+### Complementary:
+- Use MCP Tools Expert to learn tool selection
+- Use Validation Expert to fix configuration errors
+- Use Expression Syntax for dynamic field values
+- Use Workflow Patterns to understand node relationships
+
+## Testing
+
+**Evaluations**: 4 test scenarios
+
+1. **eval-001-property-dependencies.json**
+ - Tests understanding of displayOptions
+ - Guides to get_property_dependencies
+ - Explains conditional requirements
+
+2. **eval-002-operation-specific-config.json**
+ - Tests operation-aware configuration
+ - Identifies resource + operation pattern
+ - References OPERATION_PATTERNS.md
+
+3. **eval-003-conditional-fields.json**
+ - Tests unary vs binary operators
+ - Explains singleValue dependency
+ - Mentions auto-sanitization
+
+4. **eval-004-essentials-vs-info.json**
+ - Tests tool selection knowledge
+ - Explains progressive disclosure
+ - Provides success rate statistics
+
+## Success Metrics
+
+**Before this skill**:
+- Using get_node_info for everything (slow, overwhelming)
+- Not understanding property dependencies
+- Confused when fields appear/disappear
+- Not aware of operation-specific requirements
+- Trial and error configuration
+
+**After this skill**:
+- Start with get_node_essentials (91.7% success)
+- Understand displayOptions mechanism
+- Predict field visibility based on dependencies
+- Check requirements when changing operations
+- Systematic configuration approach
+- Know common patterns by node type
+
+## Coverage
+
+**Node types covered**: Top 20 most-used nodes
+
+| Category | Nodes | Coverage |
+|---|---|---|
+| HTTP/API | HTTP Request, Webhook | Complete |
+| Communication | Slack, Gmail | Common operations |
+| Database | Postgres, MySQL | CRUD operations |
+| Transform | Set, Code | All modes |
+| Conditional | IF, Switch | All operator types |
+| AI | OpenAI | Chat completion |
+| Schedule | Schedule Trigger | All modes |
+
+## Related Documentation
+
+- **n8n-mcp MCP Server**: Provides discovery tools
+- **n8n Node API**: get_node_essentials, get_property_dependencies, get_node_info
+- **n8n Schema**: displayOptions mechanism, property definitions
+
+## Version History
+
+- **v1.0** (2025-10-20): Initial implementation
+ - SKILL.md with configuration workflow
+ - DEPENDENCIES.md with displayOptions deep dive
+ - OPERATION_PATTERNS.md with 20+ node patterns
+ - 4 evaluation scenarios
+
+## Author
+
+Conceived by Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
+
+Part of the n8n-skills meta-skill collection.
diff --git a/docs/skills/n8n-automation/node-configuration/SKILL.md b/docs/skills/n8n-automation/node-configuration/SKILL.md
new file mode 100644
index 0000000..9bbfdfe
--- /dev/null
+++ b/docs/skills/n8n-automation/node-configuration/SKILL.md
@@ -0,0 +1,774 @@
+---
+name: n8n-node-configuration
+description: Operation-aware node configuration guidance. Use when configuring nodes, understanding property dependencies, determining required fields, choosing between get_node_essentials and get_node_info, or learning common configuration patterns by node type.
+---
+
+# n8n Node Configuration
+
+Expert guidance for operation-aware node configuration with property dependencies.
+
+---
+
+## Configuration Philosophy
+
+**Progressive disclosure**: Start minimal, add complexity as needed
+
+Configuration best practices:
+- get_node_essentials is the most used discovery pattern
+- 56 seconds average between configuration edits
+- 91.7% success rate with essentials-based configuration
+
+**Key insight**: Most configurations need only essentials, not full schema!
+
+---
+
+## Core Concepts
+
+### 1. Operation-Aware Configuration
+
+**Not all fields are always required** - it depends on operation!
+
+**Example**: Slack node
+```javascript
+// For operation='post'
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general", // Required for post
+ "text": "Hello!" // Required for post
+}
+
+// For operation='update'
+{
+ "resource": "message",
+ "operation": "update",
+ "messageId": "123", // Required for update (different!)
+ "text": "Updated!" // Required for update
+ // channel NOT required for update
+}
+```
+
+**Key**: Resource + operation determine which fields are required!
+
+### 2. Property Dependencies
+
+**Fields appear/disappear based on other field values**
+
+**Example**: HTTP Request node
+```javascript
+// When method='GET'
+{
+ "method": "GET",
+ "url": "https://api.example.com"
+ // sendBody not shown (GET doesn't have body)
+}
+
+// When method='POST'
+{
+ "method": "POST",
+ "url": "https://api.example.com",
+ "sendBody": true, // Now visible!
+ "body": { // Required when sendBody=true
+ "contentType": "json",
+ "content": {...}
+ }
+}
+```
+
+**Mechanism**: displayOptions control field visibility
+
+### 3. Progressive Discovery
+
+**Use the right tool for the right job**:
+
+1. **get_node_essentials** (91.7% success rate)
+ - Quick overview
+ - Required fields
+ - Common options
+ - **Use first** - covers 90% of needs
+
+2. **get_property_dependencies** (for complex nodes)
+ - Shows what fields depend on others
+ - Reveals conditional requirements
+ - Use when essentials isn't enough
+
+3. **get_node_info** (full schema)
+ - Complete documentation
+ - All possible fields
+ - Use when essentials + dependencies insufficient
+
+---
+
+## Configuration Workflow
+
+### Standard Process
+
+```
+1. Identify node type and operation
+ ↓
+2. Use get_node_essentials
+ ↓
+3. Configure required fields
+ ↓
+4. Validate configuration
+ ↓
+5. If dependencies unclear → get_property_dependencies
+ ↓
+6. Add optional fields as needed
+ ↓
+7. Validate again
+ ↓
+8. Deploy
+```
+
+### Example: Configuring HTTP Request
+
+**Step 1**: Identify what you need
+```javascript
+// Goal: POST JSON to API
+```
+
+**Step 2**: Get essentials
+```javascript
+const info = get_node_essentials({
+ nodeType: "nodes-base.httpRequest"
+});
+
+// Returns: method, url, sendBody, body, authentication required/optional
+```
+
+**Step 3**: Minimal config
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/create",
+ "authentication": "none"
+}
+```
+
+**Step 4**: Validate
+```javascript
+validate_node_operation({
+ nodeType: "nodes-base.httpRequest",
+ config,
+ profile: "runtime"
+});
+// → Error: "sendBody required for POST"
+```
+
+**Step 5**: Add required field
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/create",
+ "authentication": "none",
+ "sendBody": true
+}
+```
+
+**Step 6**: Validate again
+```javascript
+validate_node_operation({...});
+// → Error: "body required when sendBody=true"
+```
+
+**Step 7**: Complete configuration
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/create",
+ "authentication": "none",
+ "sendBody": true,
+ "body": {
+ "contentType": "json",
+ "content": {
+ "name": "={{$json.name}}",
+ "email": "={{$json.email}}"
+ }
+ }
+}
+```
+
+**Step 8**: Final validation
+```javascript
+validate_node_operation({...});
+// → Valid! ✅
+```
+
+---
+
+## get_node_essentials vs get_node_info
+
+### Use get_node_essentials When:
+
+**✅ Starting configuration** (91.7% success rate)
+```javascript
+get_node_essentials({
+ nodeType: "nodes-base.slack"
+});
+```
+
+**Returns**:
+- Required fields
+- Common options
+- Basic examples
+- Operation list
+
+**Fast**: ~18 seconds average (from search → essentials)
+
+### Use get_node_info When:
+
+**✅ Essentials insufficient**
+```javascript
+get_node_info({
+ nodeType: "nodes-base.slack"
+});
+```
+
+**Returns**:
+- Full schema
+- All properties
+- Complete documentation
+- Advanced options
+
+**Slower**: More data to process
+
+### Decision Tree
+
+```
+┌─────────────────────────────────┐
+│ Starting new node config? │
+├─────────────────────────────────┤
+│ YES → get_node_essentials │
+└─────────────────────────────────┘
+ ↓
+┌─────────────────────────────────┐
+│ Essentials has what you need? │
+├─────────────────────────────────┤
+│ YES → Configure with essentials │
+│ NO → Continue │
+└─────────────────────────────────┘
+ ↓
+┌─────────────────────────────────┐
+│ Need dependency info? │
+├─────────────────────────────────┤
+│ YES → get_property_dependencies │
+│ NO → Continue │
+└─────────────────────────────────┘
+ ↓
+┌─────────────────────────────────┐
+│ Still need more details? │
+├─────────────────────────────────┤
+│ YES → get_node_info │
+└─────────────────────────────────┘
+```
+
+---
+
+## Property Dependencies Deep Dive
+
+### displayOptions Mechanism
+
+**Fields have visibility rules**:
+
+```javascript
+{
+ "name": "body",
+ "displayOptions": {
+ "show": {
+ "sendBody": [true],
+ "method": ["POST", "PUT", "PATCH"]
+ }
+ }
+}
+```
+
+**Translation**: "body" field shows when:
+- sendBody = true AND
+- method = POST, PUT, or PATCH
+
+### Common Dependency Patterns
+
+#### Pattern 1: Boolean Toggle
+
+**Example**: HTTP Request sendBody
+```javascript
+// sendBody controls body visibility
+{
+ "sendBody": true // → body field appears
+}
+```
+
+#### Pattern 2: Operation Switch
+
+**Example**: Slack resource/operation
+```javascript
+// Different operations → different fields
+{
+ "resource": "message",
+ "operation": "post"
+ // → Shows: channel, text, attachments, etc.
+}
+
+{
+ "resource": "message",
+ "operation": "update"
+ // → Shows: messageId, text (different fields!)
+}
+```
+
+#### Pattern 3: Type Selection
+
+**Example**: IF node conditions
+```javascript
+{
+ "type": "string",
+ "operation": "contains"
+ // → Shows: value1, value2
+}
+
+{
+ "type": "boolean",
+ "operation": "equals"
+ // → Shows: value1, value2, different operators
+}
+```
+
+### Using get_property_dependencies
+
+**Example**:
+```javascript
+const deps = get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+
+// Returns dependency tree
+{
+ "dependencies": {
+ "body": {
+ "shows_when": {
+ "sendBody": [true],
+ "method": ["POST", "PUT", "PATCH", "DELETE"]
+ }
+ },
+ "queryParameters": {
+ "shows_when": {
+ "sendQuery": [true]
+ }
+ }
+ }
+}
+```
+
+**Use this when**: Validation fails and you don't understand why field is missing/required
+
+---
+
+## Common Node Patterns
+
+### Pattern 1: Resource/Operation Nodes
+
+**Examples**: Slack, Google Sheets, Airtable
+
+**Structure**:
+```javascript
+{
+ "resource": "", // What type of thing
+ "operation": "", // What to do with it
+ // ... operation-specific fields
+}
+```
+
+**How to configure**:
+1. Choose resource
+2. Choose operation
+3. Use get_node_essentials to see operation-specific requirements
+4. Configure required fields
+
+### Pattern 2: HTTP-Based Nodes
+
+**Examples**: HTTP Request, Webhook
+
+**Structure**:
+```javascript
+{
+ "method": "",
+ "url": "",
+ "authentication": "",
+ // ... method-specific fields
+}
+```
+
+**Dependencies**:
+- POST/PUT/PATCH → sendBody available
+- sendBody=true → body required
+- authentication != "none" → credentials required
+
+### Pattern 3: Database Nodes
+
+**Examples**: Postgres, MySQL, MongoDB
+
+**Structure**:
+```javascript
+{
+ "operation": "",
+ // ... operation-specific fields
+}
+```
+
+**Dependencies**:
+- operation="executeQuery" → query required
+- operation="insert" → table + values required
+- operation="update" → table + values + where required
+
+### Pattern 4: Conditional Logic Nodes
+
+**Examples**: IF, Switch, Merge
+
+**Structure**:
+```javascript
+{
+ "conditions": {
+ "": [
+ {
+ "operation": "",
+ "value1": "...",
+ "value2": "..." // Only for binary operators
+ }
+ ]
+ }
+}
+```
+
+**Dependencies**:
+- Binary operators (equals, contains, etc.) → value1 + value2
+- Unary operators (isEmpty, isNotEmpty) → value1 only + singleValue: true
+
+---
+
+## Operation-Specific Configuration
+
+### Slack Node Examples
+
+#### Post Message
+```javascript
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general", // Required
+ "text": "Hello!", // Required
+ "attachments": [], // Optional
+ "blocks": [] // Optional
+}
+```
+
+#### Update Message
+```javascript
+{
+ "resource": "message",
+ "operation": "update",
+ "messageId": "1234567890", // Required (different from post!)
+ "text": "Updated!", // Required
+ "channel": "#general" // Optional (can be inferred)
+}
+```
+
+#### Create Channel
+```javascript
+{
+ "resource": "channel",
+ "operation": "create",
+ "name": "new-channel", // Required
+ "isPrivate": false // Optional
+ // Note: text NOT required for this operation
+}
+```
+
+### HTTP Request Node Examples
+
+#### GET Request
+```javascript
+{
+ "method": "GET",
+ "url": "https://api.example.com/users",
+ "authentication": "predefinedCredentialType",
+ "nodeCredentialType": "httpHeaderAuth",
+ "sendQuery": true, // Optional
+ "queryParameters": { // Shows when sendQuery=true
+ "parameters": [
+ {
+ "name": "limit",
+ "value": "100"
+ }
+ ]
+ }
+}
+```
+
+#### POST with JSON
+```javascript
+{
+ "method": "POST",
+ "url": "https://api.example.com/users",
+ "authentication": "none",
+ "sendBody": true, // Required for POST
+ "body": { // Required when sendBody=true
+ "contentType": "json",
+ "content": {
+ "name": "John Doe",
+ "email": "john@example.com"
+ }
+ }
+}
+```
+
+### IF Node Examples
+
+#### String Comparison (Binary)
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.status}}",
+ "operation": "equals",
+ "value2": "active" // Binary: needs value2
+ }
+ ]
+ }
+}
+```
+
+#### Empty Check (Unary)
+```javascript
+{
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json.email}}",
+ "operation": "isEmpty",
+ // No value2 - unary operator
+ "singleValue": true // Auto-added by sanitization
+ }
+ ]
+ }
+}
+```
+
+---
+
+## Handling Conditional Requirements
+
+### Example: HTTP Request Body
+
+**Scenario**: body field required, but only sometimes
+
+**Rule**:
+```
+body is required when:
+ - sendBody = true AND
+ - method IN (POST, PUT, PATCH, DELETE)
+```
+
+**How to discover**:
+```javascript
+// Option 1: Read validation error
+validate_node_operation({...});
+// Error: "body required when sendBody=true"
+
+// Option 2: Check dependencies
+get_property_dependencies({
+ nodeType: "nodes-base.httpRequest"
+});
+// Shows: body → shows_when: sendBody=[true], method=[POST,PUT,PATCH,DELETE]
+
+// Option 3: Try minimal config and iterate
+// Start without body, validation will tell you if needed
+```
+
+### Example: IF Node singleValue
+
+**Scenario**: singleValue property appears for unary operators
+
+**Rule**:
+```
+singleValue should be true when:
+ - operation IN (isEmpty, isNotEmpty, true, false)
+```
+
+**Good news**: Auto-sanitization fixes this!
+
+**Manual check**:
+```javascript
+get_property_dependencies({
+ nodeType: "nodes-base.if"
+});
+// Shows operator-specific dependencies
+```
+
+---
+
+## Configuration Anti-Patterns
+
+### ❌ Don't: Over-configure Upfront
+
+**Bad**:
+```javascript
+// Adding every possible field
+{
+ "method": "GET",
+ "url": "...",
+ "sendQuery": false,
+ "sendHeaders": false,
+ "sendBody": false,
+ "timeout": 10000,
+ "ignoreResponseCode": false,
+ // ... 20 more optional fields
+}
+```
+
+**Good**:
+```javascript
+// Start minimal
+{
+ "method": "GET",
+ "url": "...",
+ "authentication": "none"
+}
+// Add fields only when needed
+```
+
+### ❌ Don't: Skip Validation
+
+**Bad**:
+```javascript
+// Configure and deploy without validating
+const config = {...};
+n8n_update_partial_workflow({...}); // YOLO
+```
+
+**Good**:
+```javascript
+// Validate before deploying
+const config = {...};
+const result = validate_node_operation({...});
+if (result.valid) {
+ n8n_update_partial_workflow({...});
+}
+```
+
+### ❌ Don't: Ignore Operation Context
+
+**Bad**:
+```javascript
+// Same config for all Slack operations
+{
+ "resource": "message",
+ "operation": "post",
+ "channel": "#general",
+ "text": "..."
+}
+
+// Then switching operation without updating config
+{
+ "resource": "message",
+ "operation": "update", // Changed
+ "channel": "#general", // Wrong field for update!
+ "text": "..."
+}
+```
+
+**Good**:
+```javascript
+// Check requirements when changing operation
+get_node_essentials({
+ nodeType: "nodes-base.slack"
+});
+// See what update operation needs (messageId, not channel)
+```
+
+---
+
+## Best Practices
+
+### ✅ Do
+
+1. **Start with get_node_essentials**
+ - 91.7% success rate
+ - Faster than get_node_info
+ - Sufficient for most needs
+
+2. **Validate iteratively**
+ - Configure → Validate → Fix → Repeat
+ - Average 2-3 iterations is normal
+ - Read validation errors carefully
+
+3. **Use property dependencies when stuck**
+ - If field seems missing, check dependencies
+ - Understand what controls field visibility
+ - get_property_dependencies reveals rules
+
+4. **Respect operation context**
+ - Different operations = different requirements
+ - Always check essentials when changing operation
+ - Don't assume configs are transferable
+
+5. **Trust auto-sanitization**
+ - Operator structure fixed automatically
+ - Don't manually add/remove singleValue
+ - IF/Switch metadata added on save
+
+### ❌ Don't
+
+1. **Jump to get_node_info immediately**
+ - Try essentials first
+ - Only escalate if needed
+ - Full schema is overwhelming
+
+2. **Configure blindly**
+ - Always validate before deploying
+ - Understand why fields are required
+ - Check dependencies for conditional fields
+
+3. **Copy configs without understanding**
+ - Different operations need different fields
+ - Validate after copying
+ - Adjust for new context
+
+4. **Manually fix auto-sanitization issues**
+ - Let auto-sanitization handle operator structure
+ - Focus on business logic
+ - Save and let system fix structure
+
+---
+
+## Detailed References
+
+For comprehensive guides on specific topics:
+
+- **[DEPENDENCIES.md](DEPENDENCIES.md)** - Deep dive into property dependencies and displayOptions
+- **[OPERATION_PATTERNS.md](OPERATION_PATTERNS.md)** - Common configuration patterns by node type
+
+---
+
+## Summary
+
+**Configuration Strategy**:
+1. Start with get_node_essentials (91.7% success)
+2. Configure required fields for operation
+3. Validate configuration
+4. Check dependencies if stuck
+5. Iterate until valid (avg 2-3 cycles)
+6. Deploy with confidence
+
+**Key Principles**:
+- **Operation-aware**: Different operations = different requirements
+- **Progressive disclosure**: Start minimal, add as needed
+- **Dependency-aware**: Understand field visibility rules
+- **Validation-driven**: Let validation guide configuration
+
+**Related Skills**:
+- **n8n MCP Tools Expert** - How to use discovery tools correctly
+- **n8n Validation Expert** - Interpret validation errors
+- **n8n Expression Syntax** - Configure expression fields
+- **n8n Workflow Patterns** - Apply patterns with proper configuration
|