Generated banner for N8n Workflow Lifecycle Official
Generated logo for N8n Workflow Lifecycle Official

N8n Workflow Lifecycle Official

n8nOfficialPublished by n8n

  • Skill
  • Workflow
  • Automation and integrations
  • Apache-2.0
  • No issues found

From the author

Use when starting, designing, organizing, finishing, or shipping an n8n workflow. Covers visual layout (sticky notes), descriptions that capture the *why*, node names, validation, testing, folders/projects, and publishing. Triggers on create_workflow_from_code, update_workflow, validate_workflow, publish_workflow, archive_workflow, "design", "lay out", "organize", "structure", "sticky", "describe this workflow", "ship", "deploy", "publish", "name this workflow", or any folder/project organization request.
— the description by n8n

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured six-stage methodology for managing n8n workflows from initial planning to production handoff. It emphasizes rigorous validation, proper documentation, and clear communication to ensure workflows are maintainable, reliable, and correctly configured. The framework covers technical best practices like node grouping, naming conventions, and folder management, while also addressing the human-in-the-loop requirements for credential verification and production readiness.

What it does

  • Guides users through a six-stage lifecycle: Plan, Build, Validate, Test, Publish, and Handoff.
  • Provides strict protocols for node naming, workflow descriptions, and visual organization using sticky notes and node groups.
  • Enforces validation checks using both automated tools and manual verification steps before publishing.
  • Manages folder organization and MCP access settings for workflow discoverability.
  • Defines clear handoff procedures to ensure users can operate and monitor workflows in production.
  • Outlines specific anti-patterns to avoid, such as incorrect assumptions about parallel execution or skipping testing.

When to use it

  • When starting a new n8n workflow project from scratch.
  • When organizing or refactoring existing workflows to improve readability and maintainability.
  • Before deploying any workflow to a production environment.
  • When troubleshooting workflows that are failing or behaving unexpectedly.
  • When coordinating with users to ensure they have the necessary context to operate a finished workflow.

Advantages

  • Reduces the likelihood of production failures by enforcing a mandatory testing and validation sequence.
  • Improves long-term maintainability through standardized naming and documentation practices.
  • Ensures workflows are properly organized and discoverable within the n8n instance.
  • Clarifies the division of responsibility between the agent and the user, particularly regarding credentials and MCP access.
  • Prevents common pitfalls like silent connection drops or incorrect assumptions about workflow execution order.

Usage examples

“Design a new workflow to sync customer data from Postgres to Slack.”
The agent initiates the 'Plan' stage, asking for specific requirements and checking for existing sub-workflows before drafting the structure.
“I need to ship this workflow to production, is it ready?”
The agent runs the validation checklist, verifies connections, asks the user to confirm credentials, and performs a test run before proceeding to publish.
“Organize my messy workflow into something readable.”
The agent applies node groups to logical steps, adds descriptive sticky notes, and updates the workflow description to capture the 'why' behind the design.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/n8n-io/n8n-workflow-lifecycle-official/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip n8n-workflow-lifecycle-official.zip -d .agents/skills

Documentation by n8n

n8n Workflow Lifecycle

The six stages

  1. PLAN. Gather requirements, ask clarifying questions, search for existing workflows / sub-workflows that already do this.

  2. BUILD. Write SDK code (with skills: subworkflows, node-config, expressions, code-nodes; readability section below). Use validate_node_config as a side-channel for iteration, debugging, or small single-node edits: clean per-parameter errors without full-graph noise. Not a replacement for validate_workflow in VALIDATE.

  3. VALIDATE. validate_workflow + get_workflow_details for connections, then have the user verify per-node credentials and create anything you couldn't (missing credentials, folders, etc).

  4. TEST. test_workflow with prepare_workflow_pin_data; iterate until output matches intent.

  5. PUBLISH. publish_workflow only after stages 3 and 4 are clean.

  6. HANDOFF. Production handoff: how to trigger it, what it returns, what to watch, what they should know to use it well.

Skipping a stage produces workflows that look done but break in production, or solve the wrong problem entirely. Three most common skips:

  • Build before plan. "User asked for X, I'll start coding" without confirming what X means, whether the same logic already exists as a sub-workflow, or which folder/project it belongs in. Cheaper to ask one clarifying question than to rebuild after.

  • Test before user-side wire-up. Running test_workflow before the user has verified credentials per node hits the wrong service or 401s. Get the user-side setup done as part of VALIDATE.

  • Publish without test. Validation passing means the SDK is well-formed; it does NOT mean the workflow is correct.

Non-negotiables

  1. Validate AND verify before publish. Run validate_workflow on the SDK code, then get_workflow_details after every create/update to check the connections object. Validation alone misses silently dropped wires. validate_node_config is a separate per-node iteration tool, not a replacement for this step.

  2. Surface known limitations to the user. If folders, MCP access, or any other limitation blocks the request, say so explicitly and propose a path. Don't silently dump workflows at the wrong location or report success on a request you couldn't fully fulfill.

  3. Ask before testing when not-auto-pinned downstreams have side effects. test_workflow auto-pins triggers, credentialed nodes, and HTTP Request nodes. Everything else (Code, Edit Fields, If, Wait, Execute Command, file ops, sub-workflow calls, Data Tables) runs for real. Ask the user before running if any of those would fire user-visible side effects. See references/TESTING.md.

Strong defaults

  • Test before publish with test_workflow + prepare_workflow_pin_data. See references/TESTING.md for mocking by trigger type, pinning individual nodes, and the side-effect surface. Looser for internal one-off scripts you watch run.

  • Always include a description on create_workflow_from_code. 1-2 sentences capturing what and why. See "Readability" below.

Validation isn't enough

validate_workflow runs schema and shape checks: missing parameters, type errors, references to non-existent nodes. It does not catch:

  • The .to()-inside-.add() connection trap (silent dropped wires)

  • Fan-outs collapsed to a single connection

  • Merge index off-by-one

  • Error outputs wired without onError: 'continueErrorOutput'

  • Parameters that are syntactically valid but semantically wrong (e.g., wrong sheet ID, wrong column name)

Validation is necessary but not sufficient. The real gate is:

  1. validate_workflow passes.

  2. get_workflow_details returns a connections object that matches your intent.

  3. test_workflow produces the right output on representative pinned data.

Only then call publish_workflow.

For the full pre-publish checklist, see references/VALIDATION_CHECKLIST.md.

Execution model

n8n workflows execute sequentially, left-to-right, top-to-bottom. Branches that visually appear parallel on the canvas (fan-out from one source to multiple downstreams) run one after the other, ordered by the target nodes' Y-position on the canvas. There is no automatic concurrency.

Practical consequences:

  • A fan-out to three slow HTTP calls runs in series; total latency is the sum, not the max.

  • "Parallel" branches share workflow state in execution order; downstream consumers see whatever the last branch left.

  • For real concurrency, dispatch sub-workflows with mode: 'each' and waitForSubWorkflow: false. See n8n-loops-official and n8n-subworkflows-official.

This is platform behavior, not an SDK quirk. Don't design fan-outs around assumed parallelism.

Naming conventions

Bad names compound: a workflow that's hard to find six months from now gets duplicated.

For full conventions (verb-noun patterns, capitalization, prefixes), read references/NAMING_CONVENTIONS.md. Short version:

  • Workflows: verb-first, scoped. Send weekly customer report not Customer report sender.

  • Nodes: describe what they do in this workflow, not the node type. Fetch active customers not Postgres1.

  • Sub-workflows: plain descriptive name (Parse RFC2822 date); carry the category in tags (subworkflow, a domain tag, tool), not a name prefix. search_workflows({ tags }) filters on them. See n8n-subworkflows-official references/NAMING_AND_DISCOVERY.md.

  • Tags: the AI-side discovery mechanism (n8n 2.27.0+). The MCP lists (list_workflow_tags), filters (search_workflows({ tags })), and attaches them (update_workflow addTags/removeTags, auto-creating unknown names). Lowercase, 2-4 per workflow. See references/NAMING_CONVENTIONS.md.

Readability: descriptions, node groups, sticky notes, conventions

Four levers carry the readability load, grouping from 7 boxes on and the other three from about 5 nodes on:

  • Workflow description: capture the why, including AI-derived context. Two sentences: what it does and why it exists. Most importantly, capture context you had during conversation that won't otherwise survive into the file (the constraint that drove the design, why this approach over the alternative, the user's reason for asking). Otherwise it dies with the chat.

  • Node groups: the only way to group nodes. Named frames, one per logical step (Validate input, Enrich order, Notify). Organization only, members run inline (reuse/isolation is a sub-workflow's job). Read references/NODE_GROUPS.md before creating or editing any group, and after any save that reported a group problem. The three things to know up front:

    • When: more than 7 boxes with every group collapsed, typically into 3-5 groups. A box is the trigger, each group, and each ungrouped node; sub-nodes and sticky notes don't count.

    • A group must be a connected, trigger-free section with at most one member facing out in each direction, with an Agent's sub-nodes all in or all out. Members are node names, not ids.

    • An invalid group op is skipped, not fatal, and unrelated edits can silently delete a valid existing group, so a save that looks clean can have lost one. The exception is a description over 145 characters: that rejects the whole update_workflow call. Read the group fields on every create and update, and repair before reporting done.

  • Sticky notes: annotate, don't group. Grouping is the node group's job (above). Stickies carry what a group name can't: a callout, a TODO, a warning. Read references/STICKY_NOTES.md before placing one.

    • On create, always anchor. sticky(content, [nodes], { color }) derives the box from the nodes it wraps, after layout. Coordinates guess at a canvas that doesn't exist yet; omitting both is worse, since the fallback centers the note under the whole graph and only dodges other stickies, never nodes. If you can't name the nodes a note is about, it doesn't belong on the canvas.

    • Three roles, and the anchor set follows from the role. A stage zone anchors one stage's nodes; a callout anchors the single node it warns about; a gray title band anchors the whole flow and is rare, only for a canvas you're deliberately leaving ungrouped.

    • Scale: under ~5 nodes, none. From 5 to 7 boxes, one zone per stage. Over 7, node groups section and document the canvas via their descriptions, and you anchor nothing to a grouped node: those positions are collapsed-chip slots, so the note draws over the expanded group and matches nothing collapsed.

    • Zones need room the derived box doesn't give. Declare height: 272 for a one-row zone, 464 for a branch fan (add 32 for Message an Agent v2+). Never declare a width. Then, once the canvas is final, widen every zone in one batched update_workflow: x = min(node.x) - 56, width = span + 112. Computed from the nodes, so re-running is harmless, but it's a finishing step, not per-edit.

    • update_workflow has no sticky op. A sticky there is an addNode of n8n-nodes-base.stickyNote with content, width, height, color in parameters; an omitted position means [0, 0].

    • Content and color: ### Title carrying the point, plus one short line. Only the top 64px clears the nodes, so never add height for more text; longer prose goes in node notes or the workflow description. color 1-7 stands for what happens there: green creates or involves a person, red is destructive or irreversible, blue is routing and transformation, gray is documentation. A user preference, then nearby workflows, outrank that.

  • Node notes for non-obvious config. Explain why a workaround exists or a Code node does what it does. Don't annotate obvious nodes.

Plus two notes:

  • Match existing project conventions before introducing your own. Skim a couple of nearby workflows via search_workflows + get_workflow_details and mirror the sticky palette, naming, and description style.

  • Layout runs on create, never on update. create_workflow_from_code lays out every node that doesn't carry an explicit position, so omit them and let it. Setting some and not others is the bad case: yours are honored and the rest are laid out around them, which is how nodes end up stacked. update_workflow runs no layout at all, so anything you add sits exactly where you put it.

Folder management

On a registered instance the MCP creates and organizes folders: create_folder, update_folder (rename/move), move_workflows_to_folder, and a folderId on create_workflow_from_code for create-time placement. search_folders resolves names to IDs.

If the user wants a folder that doesn't exist, create it, don't build at the root and report success. If the folder tools are absent, the instance isn't registered: folders are blocked in the UI too, so ask the user to register (free, in Settings) rather than create the folder by hand. No tool deletes a folder, and projects are read-only.

For the full protocol, read references/FOLDER_LIMITATIONS.md.

Per-workflow MCP access

Each workflow has an availableInMCP flag. The default depends on who created it:

  • Workflows created via the MCP (create_workflow_from_code) default to MCP-accessible. No toggle step needed: you can find them via search_workflows and operate on them right away.

  • Workflows created in the n8n UI can default to off. Until the user flips the toggle, the workflow is invisible to you.

The #1 case where this bites: the user built a workflow manually in the UI and now wants you to inspect or edit it, but you can't see it. Before assuming it doesn't exist or you're searching the wrong project, ask the user to confirm MCP access is enabled.

Sub-workflows called via MCP: the caller can use them as code-level sub-workflows without the toggle. To invoke as MCP-exposed tools, the toggle is required (and is on by default for MCP-created sub-workflows).

For the full case-by-case guide and user-facing message, read references/MCP_ACCESS_PER_WORKFLOW.md.

User-side wire-up (part of stage 3)

There are things the user has to do that you can't, and they need to be done before testing, otherwise the test fires against the wrong credential, hits a missing folder, or 401s. Surface these as a short list during VALIDATE, before TEST:

  • Verify credentials per node. newCredential('Label') is cosmetic. n8n auto-assigns the most recently edited credential of the right type, which silently picks the wrong one when the user has multiples (prod vs staging Gmail, two API keys). Tell them: "open every node that uses a credential and confirm the right one is selected." See n8n-credentials-and-security-official non-negotiable #2.

  • Create missing credentials. If the user pasted a secret in chat or the workflow needs an account that doesn't exist yet, name the credential type and have them create it in the UI.

  • Register for folders (only if the tools are missing). Folder tools need a registered instance. If they're absent, the user registers (free, in Settings) before you can create or place folders; otherwise you handle folders yourself. See references/FOLDER_LIMITATIONS.md.

  • MCP access toggle for user created workflows. Workflows you create via the MCP are MCP-accessible by default. The toggle only matters when the test depends on a UI-created workflow being callable from the MCP. See references/MCP_ACCESS_PER_WORKFLOW.md.

Don't proceed to TEST until these are confirmed done.

Handoff: production handoff (stage 6)

After publish_workflow and a clean test, the workflow is technically live, but the user still needs enough context to actually use it in production. Treat this like the freelancer-to-customer handoff: short, structured, and oriented toward how they'll operate it from here.

What to include:

  • How it triggers. Webhook URL (live now that it's published), schedule cadence + timezone, manual trigger button, sub-workflow caller, whichever applies. For webhooks, hand them the URL.

  • What it returns / where the data goes. One sentence. "Writes new rows to the customers table," "responds JSON to the caller," "fires the on-call Slack channel."

  • How to invoke it for real, with an example. "Hit the webhook with curl -X POST <url> -d '{...}'," "trigger manually from the UI," "wait until 09:00 UTC for the first scheduled run."

  • What to watch. Failure modes that surface as alerts/errors, rate-limit ceilings on upstream services, and where to look first when something breaks (executions tab, error workflow, audit log, etc.).

  • MCP access status. If you created the workflow via the MCP, it's already MCP-accessible. Let the user know they can revoke access in Settings if they want to lock it down. If they hand-built it in the UI, they need to flip MCP access on for any other agent to call it.

  • Anything still pending on their side. Secret rotation if a token was pasted in chat, follow-up wiring you couldn't reach, known TODOs left in stickies.

Keep it tight: half a dozen bullets, not a wall of text. The user shouldn't have to ask "ok, what now?"

Reference files

FileRead when
references/NAMING_CONVENTIONS.mdNaming a new workflow, sub-workflow, or node
references/NODE_GROUPS.mdBefore creating or editing any node group, or after a save reported a skipped or removed group
references/STICKY_NOTES.mdAbout to add, move, or resize a sticky note on either tool
references/FOLDER_LIMITATIONS.mdUser mentions a folder, project structure, or wants workflows organized
references/MCP_ACCESS_PER_WORKFLOW.mdBuilding a workflow that you or another agent will call via MCP
references/VALIDATION_CHECKLIST.mdJust finished a workflow and about to call publish_workflow
references/REVIEW_CHECKLIST.mdReviewing or auditing an existing workflow (any age, any author). Severity-tiered findings, distinct from the pre-publish validation checklist
references/TESTING.mdAbout to run test_workflow or execute_workflow, mocking trigger input, side-effect protocol

Anti-patterns

Anti-patternWhat goes wrongFix
Calling publish_workflow without validatingBroken workflows reach productionValidate, verify connections, then test
Creating workflows at root because the requested folder doesn't existWorkflows get lost, and the user has to drag them manuallySurface the limitation before building
Generic node names (HTTP Request1, Set2)Workflows are unreadable a month laterRename to describe the action
Missing description on create_workflow_from_codeWorkflow invisible in search, no context for maintainersAlways include 1-2 sentences
Asking the user to flip the MCP access toggle on a workflow you created via the MCPWastes their time, agent-created workflows default to MCP-accessibleOnly mention the toggle for UI-created workflows, or when the user wants to revoke MCP access on an agent-created one
Running test_workflow on a workflow with side-effecty non-pinned downstreams without askingReal Data Table write, real sub-workflow side effects, real Execute Command output, etc. Triggers + credentialed nodes + HTTP get pinned, nothing else doesAsk first. See references/TESTING.md.
No node groups on a canvas over 7 top-level boxesReader has to read every node to find what they wantGroup each logical step. See references/NODE_GROUPS.md
Reporting a create or update as done without reading its group fields (skippedGroups on create; skippedOperations / removedGroups on update)A group op was silently skipped, or an existing group was deleted by an unrelated edit, and the save still looked successfulCheck them on every call and repair. See references/NODE_GROUPS.md
Sticky note used to fake a group, or sticky-of-every-colorGrouping is the node group's job / color becomes pure noiseGroup with a node group; reserve stickies for callouts, one color per category
Hand-positioned stickies on create_workflow_from_codeCoordinates are chosen before layout runs, so the note lands away from the nodes it describesPass the nodes to sticky(content, [nodes]) and let the box be derived. See references/STICKY_NOTES.md
description: "Sends Slack."Adds nothing visible from the trigger and Slack nodeInclude why + AI-derived context: "Sends weekly summary to founders. Replaces manual report that kept getting skipped."
Designing fan-out branches as if they execute concurrentlyn8n runs fan-out branches sequentially, top-to-bottom by Y-position. Total runtime is the sum of branches, not the maxFor real concurrency, dispatch via Execute Workflow with mode: 'each' + waitForSubWorkflow: false. See n8n-subworkflows-official "Fire-and-forget parallelization"

Files

references/REVIEW_CHECKLIST.md

Workflow review checklist

Severity-tiered audit for any existing n8n workflow or n8n build (group of workflows). Different from VALIDATION_CHECKLIST.md (which is pre-publish gates for your own in-progress build): this file is for reviewing arbitrary workflows, including ones built by anyone, from anywhere.

How to use

Walk the list top to bottom. For each item, inspect the workflow (get_workflow_details) and decide if the issue applies. Report findings grouped by severity. Each item links to the canonical skill reference for the why and the how to fix. Be a very thourough reviewer and air on the side of reading references to ensure full context.

You're reviewing JSON, not SDK source. get_workflow_details returns the n8n workflow JSON (nodes with parameters, connections graph, credential references, node type strings like n8n-nodes-base.httpRequest). Phrase findings in JSON terms: "node Foo has parameters.onError set to 'continueErrorOutput' but connections.Foo.main[1] is empty,".

SeverityMeaningAction
MUST FIXShip-blocker. Security hole, broken connection, or production-breaking bug.Stop the workflow if active, fix before re-enabling.
SHOULD FIXReal issue. Antipattern, missing error handling on production paths, broken contracts.Plan a follow-up, fix in the next change.
NICE TO HAVEPolish. Naming, descriptions, conventions.Clean up opportunistically.

Cross-cutting first

Before walking the per-domain list:

  • [ ] Pull the workflow(s). get_workflow_details({ workflowId }), required so subsequent checks operate on the actual JSON, not assumptions.

  • [ ] High-level intent / logic smell test. Read the workflow's description (if it exists), then trace the happy path once top to bottom. Does the structure match what the description says it does? Anything obviously dead, missing, contradictory, or not fitting (a write node in a workflow described as read-only, a fan-out with one terminal branch that should be wired further, an HTTP call to a domain unrelated to the stated integration)? Catches whole classes of issues the per-domain checks won't surface.

  • [ ] Note the trigger type. Webhook, schedule, manual, sub-workflow, chat trigger. Severity of issues changes by trigger (a webhook-API workflow needs error paths; a manual run does not).

  • [ ] Note whether the workflow is active. Active/published workflows with broken connections are higher severity.


MUST FIX

Credentials and secrets
  • [ ] Tokens, API keys, or secrets in node text fields. Any node parameter holding Bearer xxx, sk-..., an API key, or a password as plain text. The credential system is the only correct home. → n8n-credentials-and-security-official

  • [ ] Tokens stored in Set node values for later {{$json.token}} referencing. The token is in workflow JSON regardless of how it's read. → n8n-credentials-and-security-official

  • [ ] Hardcoded credentials in Code nodes. Same leak surface as text fields. → n8n-code-nodes-official anti-patterns

  • [ ] HTTP Request nodes with Authorization header values typed in directly instead of using a credential. For Authorization: Bearer <token>, use Bearer Auth (httpBearerAuth) so the token is stored without the prefix. For other custom auth headers, use Header Auth (httpHeaderAuth). → HTTP_REQUEST_WITH_AUTH.md

  • [ ] Secret read from $vars.X and used as an auth value. Use a credential instead. → n8n-credentials-and-security-official

SQL / query injection
  • [ ] User input interpolated into a query string. Any DB node with parameters.query containing {{ $json.something }} (or any {{ ... }} expression that resolves from caller input) inside the SQL itself is a SQL injection: n8n substitutes the expression into the query before parameter binding. Use parameter binding instead (Postgres / MySQL: $1, $2 placeholders + parameters.options.queryReplacement; Mongo: object filters). → DATABASE_NODES.md

Connection bugs (silent breakage)
  • [ ] Merge index off-by-one. parameters.useDataOfInput is 1-indexed but the corresponding entry in connections.<source>.main[index] is 0-indexed. If the merge node's expected primary input doesn't match the wiring, the wrong source is picked silently. → MERGE_NODE.md

  • [ ] Merge with 3+ sources but numberOfInputs left at default 2. Third source silently drops. → MERGE_NODE.md

  • [ ] Error output wired without onError: 'continueErrorOutput' on the node config. Error branch is unreachable; node failure halts the workflow. → NODE_ERROR_OUTPUTS.md

  • [ ] onError: 'continueErrorOutput' set but main[1] not wired. Error path is enabled but goes nowhere. → NODE_ERROR_OUTPUTS.md

Webhook API workflows (Webhook + Respond to Webhook)
  • [ ] Webhook performs a sensitive action with parameters.authentication: 'none'. "Sensitive" = mutates state, sends external messages, hits production data, exposes private info, triggers paid actions. Anyone with the URL can fire it. Set parameters.authentication to 'basicAuth' or 'headerAuth' and use the matching credential. → n8n-credentials-and-security-official

Sub-workflow contracts
  • [ ] Execute Workflow Trigger set to passthrough when it shouldn't be. Passthrough loses the typed-input contract that agent tools (fromAi()) and structured callers need. Only correct when (a) the sub-workflow specifically receives binary AND isn't an agent tool, or (b) the sub-workflow takes no inputs (Define Below requires at least one field). For (b), the body should open with a Set ("Keep Only Set", no fields) and the trigger should carry a sticky noting no inputs are expected. → n8n-subworkflows-official non-negotiables and SUBWORKFLOW_PATTERNS.md "Splitting by input shape"

First-class n8n Agents

Review an Agent with get_agent (draft) and get_agent with versionId set to its published activeVersionId (live). Findings are in config JSON terms.

  • [ ] No memory block on a conversational Agent. It forgets the previous turn even inside one session. Add { "enabled": true, "storage": "n8n" }. → MEMORY.md

  • [ ] Saved sub-agent unpublished while the parent is published. Production delegation fails and the parent answers without it. Nothing else flags this. → SUB_AGENTS.md

  • [ ] Tool that sends, pays, deletes, or changes data without requireApproval (or MCP approval). → APPROVALS.md

  • [ ] Approval-required tool on an Agent that runs from Message an Agent or a scheduled task. Fails, or parks with no one to answer. → APPROVALS.md

  • [ ] Telegram channel with accessMode: public when the Agent isn't meant for strangers. Anyone can talk to it and spend its budget. → CHANNELS.md

  • [ ] Two different Agents called with the same Message an Agent session key in one project (for example both keyed on a bare Telegram id). Keys are project-wide, not per Agent, so the second Agent's call fails with "Failed to create the agent execution record". Prefix keys per Agent. → MESSAGE_AN_AGENT.md

Chat-triggered agents (Slack / Discord / Teams / Telegram)
  • [ ] Bot's own user ID not filtered out, either via the trigger's own filter option (preferred: Slack's options.userIds exclusion list) or as the first node after the trigger. The bot's reply re-triggers the workflow → infinite loop. Watch out for surface-specific semantics: Telegram's userIds is an allowlist, not an exclusion list. → CHAT_SURFACE_PATTERNS.md


SHOULD FIX

Naming
  • [ ] Generic node names (HTTP Request1, Set2, Postgres1). Debugging-hostile: a failure on node "HTTP Request3" tells the operator nothing, but node "Fetch order details" localizes the break instantly. Rename every node to describe what it does in this workflow. → NAMING_CONVENTIONS.md

Comms nodes (Slack, Gmail, Discord, SMTP, Telegram)
  • [ ] n8n attribution still appended on Slack / Gmail / Email / Discord nodes. Most comms nodes have "Append n8n Attribution" enabled by default. Users typically want it removed in production. → COMMS_NODES.md

  • [ ] Slack thread reply posting as top-level message. thread_ts not set, or set in the wrong nested location. → COMMS_NODES.md

Set node antipattern
  • [ ] Set node feeding only 0 or 1 downstream consumer. Most common antipattern in the pack. Delete + inline the expression at the consumer. → n8n-expressions-official "The Set-node antipattern"

  • [ ] Set node before a Data Table Insert/Update mapping fields to schema. Map directly in the Data Table node's per-column expression slots. → n8n-data-tables-official strong defaults

  • [ ] Set node building an email/Slack body. Build the body inline in the comms node's body field with an expression. → COMMS_NODES.md

  • [ ] Multiple consecutive Set nodes each defining one field. Collapse, or eliminate. → n8n-expressions-official

Code node antipattern
  • [ ] Code node doing pure data shaping (.map, .filter, .find, field rename, optional chaining). Use an expression or Edit Fields with arrow function. → n8n-code-nodes-official decision tree

  • [ ] Code node using crypto.createHash / crypto.createHmac. Use the native Crypto node (n8n-nodes-base.crypto). Recurring AI slip. → n8n-code-nodes-official "Cryptographic operations"

  • [ ] Code node parsing XML / SOAP / RSS. Use the native XML node + Edit Fields with arrow function for extraction. → n8n-code-nodes-official "XML / SOAP / RSS parsing"

  • [ ] Code node + Set node combo (Set builds inputs, Code transforms). One Edit Fields with arrow function does both. → ARROW_FUNCTIONS_IN_EDIT_FIELDS.md

Expression discipline
  • [ ] $json.x references deep in workflows with branches/intermediates. Switch to $('Source Node').item.json.x for refactor stability. → n8n-expressions-official non-negotiable

  • [ ] DateTime nodes used for date math/formatting. Use Luxon expressions (DateTime.fromISO(...)) inline. → n8n-expressions-official strong defaults

  • [ ] $env.X referenced in any expression. Doesn't work, throws at runtime. Replace with $vars.X (paid plans), a Data Table, or a credential for secrets. → n8n-expressions-official anti-patterns

  • [ ] Aggregate node + per-item execution mismatch. Expressions using $input.all() / $('Node').all() without combining with another node's .item should set executeOnce: true on the node. → n8n-loops-official non-negotiable and n8n-expressions-official ".all().map() triggers an executeOnce question"

Execution model
  • [ ] Workflow assumes fan-out branches execute in parallel. They don't, n8n runs them sequentially top-to-bottom by Y-position. Real concurrency needs sub-workflow dispatch with mode: 'each' + waitForSubWorkflow: false. → n8n-workflow-lifecycle-official "Execution model"

Loops
  • [ ] Loop Over Items added "to make it loop" when default per-item iteration handles it. Default per-item iteration already waits for each item before the next, so a Loop Over Items added "to wait for all items" is unnecessary. → n8n-loops-official "When NOT to reach for Loop Over Items"

  • [ ] Custom pagination implementation (Loop Over Items + $pageCount, hand-rolled while in a Code node, Set + IF cycle, etc.) instead of HTTP Request's built-in Pagination option. → HTTP_PAGINATION.md

  • [ ] Reset-mode loop with no clear termination. reset: true without an explicit stop condition + $runIndex ceiling = infinite loop, n8n eats memory until killed. → LOOP_OVER_ITEMS.md "Reset mode"

  • [ ] One Loop Over Items nested inside another in the same workflow. Doesn't work; breaks at runtime. Move the inner loop into a sub-workflow called per outer iteration (mode: 'each'). → LOOP_OVER_ITEMS.md "Nesting Loop Over Items"

Self-healing on transient failures
  • [ ] Network-calling nodes (HTTP, comms, DB, AI) without retryOnFail configured. Transient 429s and upstream blips surface as 5xx, alerts fire on noise. → n8n-error-handling-official "Self-healing on transient failures"

Switch nodes
  • [ ] Switch with no fallback output configured. Unmatched items silently drop. Set options.fallbackOutput: 'extra' and options.renameFallbackOutput: '<name>'. → SWITCH_FALLBACK.md

  • [ ] Switch outputs unnamed. Set renameOutput: true + outputKey: '<name>' per rule for self-documenting branches. → SWITCH_FALLBACK.md

Sub-workflows
  • [ ] Duplicated logic across workflows that would be a sub-workflow. → n8n-subworkflows-official decision tree

  • [ ] Sub-workflow with no description. Won't be found in future searches; nobody (or AI) knows what it does. → n8n-subworkflows-official anti-patterns

  • [ ] Sub-workflow has hidden side effects. The name and description describe pure logic (parse, validate, format, compute, transform), but the body contains write / send nodes (Data Table Insert/Update, Slack/Gmail/Discord send, HTTP POST, file write, audit log, etc.). Callers reasonably assume the sub-workflow is safe to retry; doing so creates duplicate writes or sends. Either declare the side effect (rename to e.g. Audit: or <Domain>:, document it in description, return a result the caller can branch on) or move the side effect out of this sub-workflow. → n8n-subworkflows-official "Stateless vs stateful"

  • [ ] ~30-node workflow with no extraction. Extract logical sections into sub-workflows. → n8n-subworkflows-official

AI Agents and tools
  • [ ] options.maxIterations left at default 10 on a multi-tool agent. Likely too low for modern agents with flexible tool sets; throws "Max iterations reached" workflow error. Raise to 30-50+. → AI_NODES.md "Iteration cap"

  • [ ] Generic tool names (doStuff, runQuery). Model can't tell which tool to pick, skips them or hallucinates parameters. Use verb-first specific names. → TOOLS.md

  • [ ] Default, Empty, or one-line tool descriptions. Model has no clue when to invoke. Tool descriptions are part of the prompt. → TOOLS.md

  • [ ] outputParserStructured without autoFix: true. One bad model output and the workflow fails. Set autoFix: true with a coding-capable fixer model. → STRUCTURED_OUTPUT.md

  • [ ] Tools with user-visible side effects (send, pay, refund) without human review. Wrap with slackHitlTool / discordHitlTool / telegramHitlTool / gmailHitlTool / etc. → HUMAN_REVIEW.md

  • [ ] Approval message via fromAi() instead of $tool.parameters.<name>. Model paraphrases; you approve text not values. → HUMAN_REVIEW.md

  • [ ] Hardcoded sessionId: 'default' or no sessionId on memory. All conversations share one session or sessions won't be used properly. → MEMORY.md

  • [ ] Image / audio / video generation wrapped in an Agent. Binary doesn't flow through tools or the Agent's output formatter. Use the provider's native single-call node directly. → n8n-ai-nodes-official anti-patterns

  • [ ] Agent + Switch to route on natural-language input when Text Classifier (@n8n/n8n-nodes-langchain.textClassifier) is one node with N built-in branches. → n8n-ai-nodes-official

  • [ ] Agent (or Basic LLM Chain + structured output parser) used to pull fields out of a blob of text when Information Extractor (@n8n/n8n-nodes-langchain.informationExtractor) is one node with a typed schema, no tools, no system prompt. Use the Agent only when the extraction needs tool calls or multi-turn reasoning. → n8n-ai-nodes-official

First-class n8n Agents: quality
  • [ ] Node tool without a description in its JSON. It gets a generic "Execute node" text and is picked badly. → TOOLS.md

  • [ ] Tool expression input that reads trigger or upstream data ($('Webhook'), $json.<event field>). Tools only see the model's arguments, so it's empty or wrong. → TOOLS.md "What expressions can see"

  • [ ] Vague useWhen on sub-agents or vector stores, or skill descriptions that don't say "Use when ...". These are the triggers; vague ones never fire. → SKILLS.md

  • [ ] Task with no timezone, or an objective that doesn't say where results go. Fires at the wrong hour, or produces output nobody sees. → TASKS.md

  • [ ] Slack approval where the approver needs the values (amount, recipient). Channel cards don't show parameters. → APPROVALS.md

  • [ ] MCP server without toolFilter, exposing a whole catalog when the Agent needs a few tools. → TOOLS.md

System prompts (AI Agents)

The system prompt is the load-bearing config of an agent. Severity ranges by how badly the issue degrades behavior.

  • [ ] Hardcoded date / missing date in the system prompt. Stale immediately. Agents making time-sensitive decisions (deadlines, eligibility windows, "is X expired?") get wrong answers. Use Current date: {{ $now }}. → SYSTEM_PROMPT.md "Always include the current date"

  • [ ] Long system prompt with per-tool usage instructions buried inside. Modular split violation. Brittle to edit, and tools become unreusable across agents. Move tool-specific instructions into each tool's description. → SYSTEM_PROMPT.md "The modular split" and n8n-ai-nodes-official "What goes in the system prompt vs the tool description"

  • [ ] "You are a helpful assistant" preamble with no role specifics. Generic responses, agent has no identity. Replace with a specific role and scope. → SYSTEM_PROMPT.md "What it's for"

  • [ ] No refusal/safety boundary when one is needed (the agent has tools that touch user-visible state, sends, payments). Define explicit boundaries: "only answer questions about X, redirect otherwise". → SYSTEM_PROMPT.md

Data Tables

System columns and identifiers

  • [ ] Set node before Data Table Insert/Update. Map directly in per-column slots. → n8n-data-tables-official strong defaults

  • [ ] id, createdAt, or updatedAt declared in create_data_table. System-managed; declaring them errors or shadows. → n8n-data-tables-official non-negotiable

  • [ ] Auto-id used as a cross-system identifier. Resets on table recreate / instance migration. Add a domain ID column (arxivId, customerId, requestId). → SCHEMA_DESIGN.md "system-managed columns"

_object postfix discipline (the most error-prone area)

  • [ ] Column holds stringified JSON (array, object) without the _object postfix. Readers have no contract telling them to parse. → SCHEMA_DESIGN.md "the _object convention"

  • [ ] Column has _object postfix but is string type holding native (non-stringified) values. Contract violation: postfix promises stringified content. → SCHEMA_DESIGN.md

  • [ ] Insert/Update writes to an _object column without JSON.stringify(...). A [object Object] literal lands in the row. → SCHEMA_DESIGN.md

  • [ ] Read from an _object column without JSON.parse(...). Downstream gets a string where it expects array/object; templates and tools choke. → SCHEMA_DESIGN.md

  • [ ] Sub-workflow returns _object columns as strings to callers. Storage format leaking through the interface. Parse before returning so callers receive arrays as arrays and objects as objects. → SCHEMA_DESIGN.md "Storage format ≠ interface format"

  • [ ] Sub-workflow's "fresh" path stringifies to "match" the cached path. Wrong instinct. Parse the cached path so both branches return natural shapes. → SCHEMA_DESIGN.md

  • [ ] _object column holds data that needs to be queried (filter on topics content, find rows by tag). Strings can't be queried structurally. Refactor to a relational child table or move to a real DB. → SCHEMA_DESIGN.md "When NOT to use _object"

Schema design

  • [ ] String column acting as boolean ('yes'/'no', 'true'/'false'). Use the boolean type. → SCHEMA_DESIGN.md "Picking column types"

  • [ ] Mixed casing within one table or query (createdAt AND arxiv_id together). Match camelCase to the system columns. → n8n-data-tables-official strong defaults

  • [ ] Table named with snake_case, lowercase, or in singular for a set. Title Case with spaces (Papers, Recent Events). Plural for sets, singular for one-row-per-global-thing only. → SCHEMA_DESIGN.md "Naming"

  • [ ] Boolean column not named affirmatively (completed instead of isCompleted). Affirmative names read better in filters and IFs. → SCHEMA_DESIGN.md

  • [ ] Table with more than ~17 columns. Consider splitting; the table is probably trying to be multiple things. → SCHEMA_DESIGN.md "A healthy table"

  • [ ] User-declared date column duplicates createdAt / updatedAt. Use the system columns where they fit; only add explicit date columns when the timestamp's meaning differs from "row created" or "row updated". → SCHEMA_DESIGN.md

Relational design (when the data has shape)

  • [ ] Genuine parent-child data crammed into one wide table (papers + summaries in one Papers table, customers + orders in one Customers table). Split into parent + child tables, reference parent by id (paperId, customerId). → SCHEMA_DESIGN.md "Designing relationally" and n8n-data-tables-official "Relational design"

  • [ ] Child arrays stored in _object columns when they need to be queried or filtered individually. Refactor to a relational child table. → SCHEMA_DESIGN.md "When NOT to use _object"

  • [ ] Cascade strategy unclear (no defined behavior on parent delete). Pick one per relationship: cascade-delete via a separate Delete on children, soft-delete (archived flag), or orphan. Mixed strategies cause silent bugs. → SCHEMA_DESIGN.md "Enforce integrity in the workflow"

  • [ ] Multi-table write sequence (parent Insert + child Insert) without partial-failure handling. No transactions exist; a child failure leaves the parent orphaned. Pick: compensating writes, idempotent retry with upsert + stable domain IDs, or soft state marker (status: 'pending' → 'complete'). → SCHEMA_DESIGN.md "No transactions, plan for partial failure"

  • [ ] Child rows pointing at deleted parents (orphan detection missing). Either soft-delete by default, or run a periodic cleanup workflow. → SCHEMA_DESIGN.md "Watch for child without parent"

  • [ ] 3+ joined tables with transactional writes. Past Data Tables' wheelhouse. Use a real SQL DB. → SCHEMA_DESIGN.md "When Data Tables are the wrong tool"

Operation gotchas

  • [ ] Multi-column filter without explicit matchType: 'allConditions'. Defaults to anyCondition (OR) in some versions; surprising when intent is AND. → OPERATIONS.md "matchType"

  • [ ] Get without alwaysOutputData: true followed by an IF that branches on result presence. No-match produces zero items, the IF doesn't fire at all. → DEDUP_PATTERNS.md "Pattern 3: Get + IF"

  • [ ] Get + IF branches produce different JSON shapes (cached path vs. freshly-processed path). Downstream $json.x resolves to the wrong field depending on which fired. Insert a Set/NoOp anchor to normalize. → n8n-expressions-official "Combine Inputs convergence"

  • [ ] Update with no match silently does nothing. No error. Either follow with a Get to confirm, or use upsert for create-or-update. → OPERATIONS.md "Update with no match"

  • [ ] Insert with no dedup in a workflow that can re-fire (webhook retry, scheduled re-run). Creates duplicates. Use upsert, rowNotExists + Insert, or upstream dedup. → DEDUP_PATTERNS.md

  • [ ] returnAll: false without explicit limit. Defaults to 50 in many versions; downstream may expect more and silently truncate. → OPERATIONS.md "returnAll"

  • [ ] Plain "have I seen this value?" dedup using a Data Table. The Remove Duplicates node ("items seen in previous executions" mode) handles this with no schema. Reach for Data Tables only when the dedup state needs to be queryable, has row-level logic, or has TTL/per-tenant scoping. → DEDUP_PATTERNS.md

  • [ ] Idempotency-key workflow without TTL cleanup. Markers shouldn't live forever; run a daily cleanup. → DEDUP_PATTERNS.md "Idempotency keys"

Mapping mode and evolution

  • [ ] autoMapInputData mode without a stable 1:1 between upstream field names and column names. Drift on either side silently breaks the mapping. Default to defineBelow. → SCHEMA_DESIGN.md "Mapping mode"

  • [ ] Workflow attempts an in-place column type change. Not supported. Add a new column, copy via workflow, drop the old, rename the new. → SCHEMA_DESIGN.md "Schema evolution"

Wrong tool for the job

  • [ ] Cross-app shared data in Data Tables. Awkward to query from outside n8n. Use a real DB. → SCHEMA_DESIGN.md "When Data Tables are the wrong tool"

  • [ ] Millions of rows or write-heavy volume in a Data Table. Performance degrades; use a real DB. → SCHEMA_DESIGN.md

Binary handling
  • [ ] Reading file content from $json instead of $binary. → BINARY_BASICS.md

  • [ ] Agent tool returning raw binary directly. Tool output is JSON-only. Upload to storage, return key/URL in JSON. → AGENT_TOOL_BINARY.md

  • [ ] Uploaded chat files passed to a tool via fromAi. fromAi doesn't carry binary. Pre-stage to storage, inject keys in the system prompt. → AGENT_TOOL_BINARY.md

  • [ ] Binary lost after a JSON transform. Use Merge to combine the JSON output with the binary stream. → MERGE_FOR_CONTEXT.md

  • [ ] Image sent to a chat surface from raw $binary. Chat surfaces need a URL-referenced image (or platform-native file upload). → CDN_REQUIREMENT.md

Public trigger auth
  • [ ] Webhook trigger with parameters.authentication: 'none' even on read-only / lookup paths. The URL is publicly callable; no auth invites abuse (rate-limit exhaustion, scanning, scraping). Use Basic / Header / JWT auth unless the workflow is genuinely meant to be public. (If the action is sensitive, this is a MUST FIX, see above.) → n8n-credentials-and-security-official

  • [ ] Form Trigger fronting a sensitive action with no auth gate. Forms that create accounts, mutate data, send external messages, or trigger paid operations should require real auth at the trigger. An obscure URL is not auth. → n8n-credentials-and-security-official

Webhook / Respond to Webhook
  • [ ] Fallible nodes with no error path. HTTP / DB / API / file nodes need output(1) wired to a 5xx Respond. Without it, the failure is unhandled: the workflow halts and the caller gets whatever n8n's generic error response is (no controlled status code, no useful body), and the operator only learns about it if a workflow-level error workflow is configured separately. → n8n-error-handling-official non-negotiables and API_WORKFLOWS.md

  • [ ] Error response returns 200. Caller's HTTP client treats it as success; downstream error handling never fires. Always 4xx (caller error) or 5xx (server error). → RESPONSE_SHAPES.md

  • [ ] Generic 500 for every failure. Validation errors should be 400, auth issues 401/403, conflicts 409, rate limits 429. Caller can't distinguish their bug from your outage. → RESPONSE_SHAPES.md

  • [ ] respondWith: 'json' body using JSON.stringify(...) instead of an object literal (produces double-encoded body). → WEBHOOK_NODES.md

HTTP Request
  • [ ] Headers set both via headerParameters and a credential's httpHeaderAuth. They conflict. → HTTP_NODES.md

  • [ ] Untrusted external API call without options.timeout set. Runtime default is 5 minutes; a hung request stalls the workflow. → HTTP_NODES.md

Schedule trigger
  • [ ] Business-critical schedule with no explicit timezone at the workflow level. DST and instance moves cause timing shifts. → TRIGGER_NODES.md

  • [ ] Schedule-triggered workflow not idempotent for missed-run scenarios. Restarts or downtime can miss runs. → TRIGGER_NODES.md


NICE TO HAVE

Naming
  • [ ] Workflow name doesn't follow verb-first pattern (Send weekly customer report vs. Customer report sender). → NAMING_CONVENTIONS.md

  • [ ] Untagged sub-workflow (missing subworkflow, a domain tag, or tool). Tags are the discovery mechanism; an untagged sub-workflow won't surface under any tags filter. → NAMING_AND_DISCOVERY.md

Readability
  • [ ] Canvas over 7 top-level boxes (n8n 2.28+) whose logical steps aren't grouped into node groups. Boxes are the trigger, each group, and each ungrouped node; sub-nodes and stickies don't count. Collapsed groups make the canvas read as steps, not nodes. Skip on older instances, where node groups don't exist. → NODE_GROUPS.md

  • [ ] Group with no description, or one written to restate its title. Groups are created collapsed, so title plus description is all a reader gets. Max 145 characters. → NODE_GROUPS.md

  • [ ] An Agent grouped apart from its sub-nodes. Any non-main connection crossing the boundary drops the group; all in or all out. → NODE_GROUPS.md

  • [ ] Sticky note used to fake a group instead of a node group. Stickies annotate (callouts, TODOs, warnings); they don't group. → SKILL.md "Readability"

  • [ ] Sticky title re-states what's visible (Set, If, Set). Title with the callout's point. → SKILL.md "Readability"

  • [ ] Sticky color used for variety instead of meaning. Color should say what happens there (green creates or involves a person, red destructive or irreversible, blue routing, gray documentation), one meaning per color, matching whatever nearby workflows already use. → STICKY_NOTES.md

  • [ ] Sticky sitting away from, or on top of, what it describes. On create, anchor it with sticky(content, [nodes]) instead of positioning by hand. A note at [0, 0] is the usual tell, though saved JSON can't distinguish an omitted position from a deliberate one, so check where it should sit rather than how it got there. → STICKY_NOTES.md

  • [ ] Workflow description is empty, one sentence, or doesn't capture the why. Two sentences: what it does, why it exists. → SKILL.md "Readability"

  • [ ] Code node without a one-line note explaining its purpose. → SKILL.md "Readability"

  • [ ] Workflow ignores existing house style (sticky palette, naming convention used in nearby workflows). Match what's there. → SKILL.md "Readability"

Sub-workflow conventions
  • [ ] Sub-workflow without a final Return Set / Edit Fields node shaping the return contract. The legitimate exception to the Set-node antipattern. → n8n-subworkflows-official "Other conventions"

  • [ ] Inputs / outputs not documented in the sub-workflow's description. Field names, types, purpose. → n8n-subworkflows-official "Sub-workflow inputs and outputs"

Conventions
  • [ ] Per-execution context (user identity, files, current task) buried in a static system prompt instead of injected via expressions or piecing. Hard to update. → SYSTEM_PROMPT.md "Storing the prompt"

  • [ ] Generic safety boilerplate language in the system prompt for risks the model already handles. Reinforcement adds tokens without changing behavior. Reserve safety language for specific, named risks. → SYSTEM_PROMPT.md anti-patterns


Reporting findings

When reporting, group by severity, then within severity by domain. For each finding include:

  • The specific node(s) or section affected.

  • A one-sentence description of the issue.

  • The link to the canonical skill ref for the fix.

MUST FIX
  Security
  - Node `Send Webhook`: bearer token in headerParameters value field. → n8n-credentials-and-security-official
  - Node `Lookup user`: SQL string concat with $json.email. → DATABASE_NODES.md

  Connections
  - Node `Validate input`: `connections.Validate input.main[0]` has only 1 entry, but the workflow's logic expects fan-out to 3 downstreams. → check the SDK code (likely a missed `.add(...)` call)

SHOULD FIX
  ...

A review agent should not auto-fix MUST FIX items without user confirmation: security and connection fixes have blast radius and the user should know what's changing.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an API key, Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
n8n
Publisher
n8n (Official)
Source
n8n-io/skills · skills/n8n-workflow-lifecycle-official · commit 27b6b9f
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by n8n or n8n.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download n8n-workflow-lifecycle-official.zip