N8n Workflow Patterns
davila7/claude-code-templates
Proven workflow architectural patterns from real n8n workflows.
Validates and fixes n8n's double-curly-brace expressions, covering $json, $node, and the webhook body-wrapping mistake.
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntax --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/n8n-expression-syntax .claude/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .claude/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntaxType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntax --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/n8n-expression-syntax .agents/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .agents/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntax --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/n8n-expression-syntax .cursor/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .cursor/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/czlonkowski/n8n-skills.git --path skills/n8n-expression-syntax--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntax --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/n8n-expression-syntax .gemini/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .gemini/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntaxInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/n8n-expression-syntax .github/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .github/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install czlonkowski/n8n-skills n8n-expression-syntax --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czlonkowski/n8n-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/n8n-expression-syntax .opencode/skills/n8n-expression-syntax && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "n8n-expression-syntax" agent skill from https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-expression-syntax into .opencode/skills/n8n-expression-syntax/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "n8n-expression-syntax", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
n8n-expression-syntaxValidates and fixes n8n's double-curly-brace expressions, covering $json, $node, and the webhook body-wrapping mistake.
n8n workflow expressions written with double curly braces, the mechanism nodes use to pass data to each other, get checked against the error patterns that most often break a workflow: wrong node-name quoting, confusing $json with $node, and forgetting that a webhook's incoming payload sits under a .body property rather than at the root.
It also documents when $env access is blocked by an instance setting and what to use instead, when an expression is complex enough to affect performance, and why Code nodes need plain JavaScript rather than expression syntax at all. Bundled reference files hold the common-mistakes list and worked examples in full.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 19cd793. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are javascript and json).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
n8n Expression Syntax loads about 5.2k tokens when it runs. Until then it costs about 138 tokens; SKILL.md has 1,974 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from czlonkowski/n8n-skills at commit 19cd793, republished under its MIT licence (© czlonkowski). 1,974 words, ~5,171 tokens.
.claude/skills/n8n-expression-syntax/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.Expert guide for writing correct n8n expressions in workflows.
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)Access data from the current node:
{{$json.fieldName}}
{{$json['field with spaces']}}
{{$json.nested.property}}
{{$json.items[0].name}}Access data from any previous node:
{{$node["Node Name"].json.fieldName}}
{{$node["HTTP Request"].json.data}}
{{$node["Webhook"].json.body.email}}Important:
Access current date/time:
{{$now}}
{{$now.toFormat('yyyy-MM-dd')}}
{{$now.toFormat('HH:mm:ss')}}
{{$now.plus({days: 7})}}Access environment variables:
{{$env.API_KEY}}
{{$env.DATABASE_URL}}Warning: Some n8n instances have N8N_BLOCK_ENV_ACCESS_IN_NODE enabled, which blocks $env access entirely. If $env returns errors, use alternative approaches:
Most Common Mistake: Webhook data is NOT at the root!
{
"headers": {...},
"params": {...},
"query": {...},
"body": { // ⚠️ USER DATA IS HERE!
"name": "John",
"email": "john@example.com",
"message": "Hello"
}
}❌ 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.
// 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']}}// 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}}// 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}}"
}Code nodes use direct JavaScript access, NOT expressions!
// ❌ 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();// ❌ WRONG
path: "{{$json.user_id}}/webhook"
// ✅ CORRECT
path: "user-webhook" // Static paths only// ❌ WRONG
apiKey: "={{$env.API_KEY}}"
// ✅ CORRECT
Use n8n credential system, not expressionsBefore you add any node — or write any code — to transform data, walk this order and stop at the first that fits:
Expression ({{ ... }}) in the consuming field. Property access, method chains (.map().filter().join()), ternaries, string building, Luxon date math — if it's "take A, produce B" without intermediate variables, it's an expression. This covers most "just transform this" cases.
$jmespath() inside that same expression, before you split into items or chain .map().filter(). One query replaces a Split Out → Filter → Aggregate chain. Rules below.Arrow-function IIFE inside an Edit Fields field. When the logic needs intermediate variables, branching, or comments but still operates on one item, wrap it in an immediately-invoked arrow function right in the field value:
={{ (() => {
const items = $json.line_items;
const subtotal = items.reduce((sum, it) => sum + it.price * it.qty, 0);
const tax = subtotal * 0.08;
return (subtotal + tax).toFixed(2);
})() }}The outer (...) brackets the function; the trailing () invokes it. Drop either and n8n refuses to run. Inside you get the full expression scope ($json, $('Node'), $now, Luxon) plus const/let, if/switch, try/catch, and regex. No require, no await.
Code node — last resort. Only when you need multi-item aggregation across the whole dataset ($input.all()), an allowlisted library, or async work.
Why the order matters. It's not style — it's readability and performance. The Code node runs in a sandboxed VM with per-invocation setup and value marshaling — a cold-start cost that can reach 500–1000ms before your logic runs. (It amortizes on warm, high-item-count runs, so treat this as the common-case cost, not a universal constant.) The same logic in an expression or Edit Fields IIFE runs in-process in single-digit milliseconds and skips the sandbox entirely. For pure single-item shaping that's a large gap with no functional difference, and it compounds on hot paths like per-request webhooks. The expression also stays visible in the field that uses it, instead of hiding in an upstream node someone has to open to understand. Reach past a stage only when the input or scope genuinely demands it.
$jmespath() — query nested JSON in one expression{{ $jmespath($json, "customers[?country=='PL' && revenue > `100000`].name") }} → ["Acme"]Verified on n8n 2.38: this one expression returns exactly what Split Out → Filter → Aggregate returns, with no extra nodes. The syntax is unforgiving, and most mistakes fail silently:
| Write | Not | What the wrong form does |
|---|---|---|
$jmespath(object, "query"), object first | $jmespath("query", object) | throws expected two arguments (Object, string) for this function (JMESPath's own docs show search(query, data)) |
strings in single quotes: country=='PL' | country=="PL" | double quotes mean a field name → returns [], no error |
numbers/booleans in backticks: revenue > `100000` | revenue > 100000 | parse error → whole expression becomes null (see Debugging) |
&& || ! == | and or = | parse error → null |
hyphenated keys quoted: 'customers[*].contact."first-name"' (single-quote the JS string) | contact.first-name | parse error → null |
over items, keep the wrapper: $jmespath($('Node').all(), "[?json.country=='PL'].json.name") or $jmespath($input.all().map(i => i.json), "[?country=='PL'].name") | "[?country=='PL'].name" on .all() | items are {json: …} wrappers → [] |
null; filter with no match → []; sum() over an empty
projection → 0.undefined ($json.missingField) throws the same expected two arguments error.
That one does fail the node.length(), sum(), max_by(arr, &field), sort_by(arr, &field),
reverse(), contains(), starts_with(), keys(), to_number(); projection [*], flatten
[], pipe | [0], reshape {name: name, email: contact.email}.$jmespath in
Edit Fields and follow with a single Split Out on that field.A Set / Edit Fields node whose only job is to extract a value and hand it to one downstream node is dead weight. Inline its expression at the consumer instead.
❌ Webhook → Set { customer_id: {{ $json.body.customer_id }} } → Postgres: WHERE id = {{ $json.customer_id }}
✅ Webhook → Postgres: WHERE id = {{ $('Webhook').item.json.body.customer_id }}The Set node adds a hop, more canvas clutter, and a refactor hazard, while doing nothing the consumer couldn't do itself. To remove it cleanly with n8n_update_partial_workflow: rewire the connection (removeConnection from the Set's source-and-target, addConnection straight from source to consumer), patchNodeField the consumer's expression to reference the original source by node name, then removeNode the Set.
Quick test: count how many downstream nodes reference each field the Set produces.
Legitimate exceptions — keep the Set when:
Include Other Fields: false it whitelists the output shape so internal scratch fields don't leak.When branches converge (after IF/Switch/Merge), $json becomes "whichever branch fired last" — non-deterministic, and a silent source of wrong data. Insert a NoOp node at the convergence, name it descriptively (Combine Inputs), and have downstream nodes reference it by name:
Branch A ──┐
├─→ [NoOp: Combine Inputs] ──→ downstream uses $('Combine Inputs').item.json.x
Branch B ──┘The NoOp survives refactors: inserting a transform later between it and the consumer doesn't break the $('Combine Inputs') reference. (If the branches produce different shapes, use a Set node instead of a NoOp to normalize both into one shape — see the exceptions above.)
More broadly in branchy flows, prefer $('Node').item.json.x over deep $json.x. $json breaks the moment an intermediate node is inserted or a node clears item context (Aggregate, Code with Run for All, branching merges); the failure is silent and downstream gets the wrong data with no error. A node-name reference is unambiguous regardless of what sits between source and consumer.
Expressions must be wrapped in double curly braces.
❌ $json.field
✅ {{$json.field}}Field or node names with spaces, diacritics, or special characters require bracket notation:
❌ {{$json.field name}}
✅ {{$json['field name']}}
❌ {{$node.HTTP Request.json}}
✅ {{$node["HTTP Request"].json}}
// Bracket notation is mandatory for keys with special characters
✅ {{$json['Gross Price w/o shipment']}}
✅ {{$json['Cena brutto zł']}}Node references are case-sensitive:
❌ {{$node["http request"].json}} // lowercase
❌ {{$node["Http Request"].json}} // wrong case
✅ {{$node["HTTP Request"].json}} // exact matchDon't double-wrap expressions:
❌ {{{$json.field}}}
✅ {{$json.field}}For complete error catalog with fixes, see COMMON_MISTAKES.md
| 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 |
For real workflow examples, see EXAMPLES.md
Webhook receives:
{
"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}}HTTP Request returns:
{
"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}}// 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// First item
{{$json.users[0].email}}
// Array length
{{$json.users.length}}
// Last item
{{$json.users[$json.users.length - 1].name}}// Dot notation (no spaces)
{{$json.user.email}}
// Bracket notation (with spaces or dynamic)
{{$json['user data'].email}}// Concatenation (automatic)
Hello {{$json.name}}!
// String methods
{{$json.email.toLowerCase()}}
{{$json.name.toUpperCase()}}// Direct use
{{$json.price}}
// Math operations
{{$json.price * 1.1}} // Add 10%
{{$json.quantity + 5}}// Ternary operator
{{$json.status === 'active' ? 'Active User' : 'Inactive User'}}
// Default values
{{$json.email || 'no-email@example.com'}}// 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')}}// Substring
{{$json.email.substring(0, 5)}}
// Replace
{{$json.message.replace('old', 'new')}}
// Split and join
{{$json.tags.split(',').join(', ')}}A common worry is that a complex {{ }} is slow. It isn't — what costs is how many times n8n evaluates an expression, not how elaborate each one is.
Measured on an n8n 2.x instance, an elaborate expression (sqrt, split, reduce, arithmetic) costs the same per item as a trivial {{ $json.x > 50 }} — roughly ~0.2 ms/item either way, because ~90% of that is n8n building the per-item evaluation context, not running your expression.
What this means in practice:
nullVerified on n8n 2.38 with the default expression runtime: during execution the handler around
each {{ }} re-throws only n8n's own ExpressionErrors and swallows other JavaScript errors, so
the field resolves to empty/null and the node still reports success. $json.missing.field
(TypeError), JSON.parse('{bad'), throw new Error(...) and JMESPath syntax errors all
produced null. In a Filter or IF condition every item then silently fails the check. On other
versions or expression engines the same mistake may fail the node instead. Either way, never
trust a green run on its own.
This isn't a reason to avoid expressions (a Code node has silent traps of its own). It's a reason to test with real items:
null where you expected data is the symptom.
validate_workflow and a green execution won't tell you.{{ (() => { try { return JSON.stringify(<expr>) } catch (e) { return 'ERROR: ' + e.message } })() }}$jmespath given a non-object argument).These appear in the editor preview; at runtime most of them resolve to null instead (see above).
"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
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()+, -, *, /, %JSON query:
$jmespath(object, "query"): filter/pick/aggregate nested JSON (see the $jmespath() section for the quoting rules).bodyEssential Rules:
.body{{ }} become null silently. Check output values after a test run$jmespath(object, "query"): 'string', `number`, "field"; json. prefix over .all()Most Common Mistakes:
{{$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:
Need Help? Reference the n8n expression documentation or use n8n-mcp validation tools to check your expressions.
© czlonkowski, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 3 other files in skills/n8n-expression-syntax of czlonkowski/n8n-skills.
Open the folder on GitHubat commit 19cd793
n8n Expression Syntax next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| n8n Expression Syntax this skillczlonkowski/n8n-skills | 6.4k | — | ~5.2k | Automated safety check: Pass | MIT | |
| N8n Workflow Patternsdavila7/claude-code-templates | 32k | 1 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Automation Flowsericrisco/rsc-harness | 156 | — | ~2.9k | Automated safety check: Pass | MIT | |
| N8n Trigger Testing Strategiesproffesor-for-testing/agentic-qe | 494 | — | ~3.7k | Automated safety check: Pass | MIT | |
| N8n Workflow BuilderFerroxLabs/wayland | 608 | — | ~3.3k | Automated safety check: Pass | Apache-2.0 | |
| N8n Expression Syntaxdavila7/claude-code-templates | 32k | 4 repos | ~2.4k | Automated safety check: Pass | MIT |
davila7/claude-code-templates
Proven workflow architectural patterns from real n8n workflows.
ericrisco/rsc-harness
A skill your agent uses when building or fixing a no-code automation on n8n, Make, or Zapier — trigger to multi-app steps with data mapping, dedup, retries and an error path — or picking the…
proffesor-for-testing/agentic-qe
Webhook testing, schedule validation, event-driven triggers, and polling mechanism testing for n8n workflows.
FerroxLabs/wayland
Expert n8n workflow automation covering self-hosted deployment, node configuration, trigger types, webhook handling, data transformation with expressions, error handling patterns, credential…
davila7/claude-code-templates
Validate n8n expression syntax and fix common errors. An agent skill from davila7/claude-code-templates.
vibeeval/vibecosystem
n8n otomasyon workflow'lari. An agent skill from vibeeval/vibecosystem.
czlonkowski/n8n-skills
Explains how n8n keeps file bytes in $binary apart from structured $json data, and how to read, write and preserve binary across nodes, agent tools and chat.
czlonkowski/n8n-skills
Guides writing JavaScript in n8n Code nodes: picking an execution mode, reading input data, returning items, using built-in helpers and avoiding common errors.
czlonkowski/n8n-skills
Explains how to write native Python in n8n Code nodes, including the two input variables, blocked imports and fixes for common errors.
czlonkowski/n8n-skills
Explains the n8n Custom Code Tool's actual runtime contract so an AI-agent-callable tool doesn't get written like a regular workflow Code node.
czlonkowski/n8n-skills
Wires n8n workflows so failures are visible and recoverable: per-node error outputs, retries, error workflows and correct 4xx and 5xx webhook responses.
czlonkowski/n8n-skills
Keeps an n8n MCP session pointed at the right n8n instance, with rules for discovering, switching and verifying the target before credential writes and for recovering from misroutes.
Works with
Categories
Validates and fixes n8n's double-curly-brace expressions, covering $json, $node, and the webhook body-wrapping mistake. body property rather than at the root.
n8n Expression Syntax fits situations like: debugging a workflow field that isn't picking up the expected data; referencing a webhook's body, headers, or query parameters correctly; checking whether a complex expression is likely to hurt performance.
Run `npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a claude-code`. Or copy the skill folder (skills/n8n-expression-syntax in czlonkowski/n8n-skills) into .claude/skills/n8n-expression-syntax in your project. Claude Code loads it when a task matches its description.
Run `npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a codex`. Or copy the skill folder (skills/n8n-expression-syntax in czlonkowski/n8n-skills) into .agents/skills/n8n-expression-syntax in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add czlonkowski/n8n-skills --skill n8n-expression-syntax -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/n8n-expression-syntax, .gemini/skills/n8n-expression-syntax, .github/skills/n8n-expression-syntax and .opencode/skills/n8n-expression-syntax in your project.
Going by SKILL.md and its folder, n8n Expression Syntax needs credentials named API_KEY.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
n8n Expression Syntax is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.2k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with n8n Expression Syntax: N8n Workflow Patterns (davila7/claude-code-templates, 32k stars), Automation Flows (ericrisco/rsc-harness, 156 stars), N8n Trigger Testing Strategies (proffesor-for-testing/agentic-qe, 494 stars) and N8n Workflow Builder (FerroxLabs/wayland, 608 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
czlonkowski (a GitHub user) maintains it in czlonkowski/n8n-skills, which has 6,387 GitHub stars. The repository holds 15 skills in this directory. The repository was last updated on September 16, 2026.
Source: czlonkowski/n8n-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.