> n8n-workflow-sdk
Builds, tests, validates, and manages n8n workflows programmatically using the @n8n/workflow-sdk. Activates when the user wants to create n8n workflows with code, convert between JSON and code, validate workflows, generate test/pin data, build AI agent workflows, or work with the n8n workflow SDK in any capacity. Also triggers on mentions of workflow-sdk, workflow builder, n8n workflow code, workflow JSON, workflow validation, code generation, or programmatic workflow creation.
curl "https://skillshub.wtf/geckse/n8n-skills/n8n-workflow-sdk?format=md"n8n Workflow SDK
Build, test, validate, and manage n8n workflows programmatically using the @n8n/workflow-sdk. This skill enables code-based workflow construction with full validation, JSON round-tripping, and testing support.
When You Need More Detail
This skill uses progressive disclosure. The SKILL.md covers the full workflow, decision-making, and quick-reference API. For complete patterns and examples, read these reference files:
references/workflow-building.md— Workflow creation, settings, JSON import/export, and complete building patternsreferences/nodes-and-connections.md— All node factories, subnodes (AI/LangChain), connection patterns, and credentialsreferences/control-flow.md— IF/Else, Switch/Case, Split in Batches, Merge, error handling, and branchingreferences/expressions-and-code.md— Expression system, Code node helpers, fromAi, and expression contextreferences/validation-and-testing.md— Workflow validation, pin data, test data generation, and testing strategiesreferences/code-generation.md— JSON-to-code, code-to-JSON, parseWorkflowCode, and the generation pipelinereferences/plugins-and-advanced.md— Plugin system, type generation, Zod schemas, and extensibilityreferences/node-registry-official.json— Cached registry of all 556 official n8n nodes (name, version, description)references/node-registry-community.json— Cached registry of community nodes (name, version, packageName)references/node-registry-properties.jsonl— Node properties/parameters (one JSON line per node, grep for a specific node)
Read the appropriate reference file before writing any code.
Project Setup
Before writing SDK code, the project needs @n8n/workflow-sdk and TypeScript tooling installed. If the project already has these, skip this section.
For a new project:
mkdir my-workflow-project && cd my-workflow-project
npm init -y
npm install @n8n/workflow-sdk
npm install --save-dev typescript tsx @types/node
For an existing project:
npm install @n8n/workflow-sdk
npm install --save-dev typescript tsx @types/node
Running workflow scripts:
npx tsx src/my-workflow.ts
The SDK is a pure TypeScript/JavaScript library — no build step is required. tsx runs .ts files directly. If the project already uses ts-node or another TypeScript runner, that works too.
CRITICAL: Node Type Lookup — Never Guess Node Types
DO NOT invent or guess node type identifiers. n8n has hundreds of nodes, each with a specific type string (e.g., n8n-nodes-base.slack). Using a wrong or made-up type produces a workflow that fails in n8n.
Always look up the real node type from the cached node registry before using it.
Local Registry Cache (Primary Source)
This skill ships with cached copies of the n8n node registries. Read these files to look up node types — no network requests needed:
references/node-registry-official.json— Index of all 556 built-in n8n nodes (name, version, description)references/node-registry-community.json— Index of community-contributed nodes (name, version, packageName)references/node-registry-properties.jsonl— Full node properties/parameters (one JSON line per node)
Index files (for finding the right node)
Each official node entry has:
name— The node type identifier to use innode()/trigger()(e.g.,"n8n-nodes-base.slack")displayName— Human-readable name (e.g.,"Slack")version— Current version numberdescription— What the node doesgroup—"[\"trigger\"]"or other classificationalias— Alternative names for search (e.g.,["message", "chat"])categories— Node categories (e.g.,["Communication"])
Each community node entry has:
name— The node type identifierdisplayName— Human-readable namepackageName— The npm package the user must install (e.g.,"@mendable/n8n-nodes-firecrawl")version— Version numberisOfficialNode— Whether verified by n8n
How to find a node: Read the appropriate registry JSON index and search for the node by displayName, name, alias, or description.
Properties file (for configuring a node)
Once you've found a node's name in the index, grep the properties file to get its available parameters:
grep 'n8n-nodes-base.slack' references/node-registry-properties.jsonl
Each line is a JSON object: {"node": "n8n-nodes-base.slack", "properties": [...]} where properties contains the node's parameter definitions — field names, types, defaults, options, required flags, and conditional display rules. Use this to correctly configure the parameters object in node() / trigger().
CRITICAL: Use option value, not display name. For options-type parameters, the properties JSONL lists both a display name (what the UI shows) and the actual value (what goes in the workflow JSON). These are often different — you must use the value. Common traps:
| Node | Parameter | Display Name | Actual Value |
|---|---|---|---|
| Set | mode | JSON | raw |
| HTTP Request | contentType | Form Urlencoded | form-urlencoded |
| Code | language | Python | pythonNative |
| Webhook | responseMode | Immediately | onReceived |
| Merge | mode | SQL Query | combineBySql |
CRITICAL: Set node v3.3+/3.4 uses assignments, NOT fields. The parameter schema changed at v3.3. If you're using Set node v3.4, you MUST use assignments.assignments (with type: 'string'/'number'/'boolean' and a single value field). The old fields.values / stringValue / numberValue format is for v3.0–3.2 only. See nodes-and-connections.md for full examples.
Deriving version hints from properties data
When the registry cache version field appears to be a rounded major version (e.g., 4 instead of 4.4), you can derive the actual latest version from @version hints in the properties JSONL. About 96 of 554 nodes include these hints.
Node properties use displayOptions.show["@version"] and displayOptions.hide["@version"] to control which fields appear for which versions. These arrays contain the version numbers the node supports, letting you infer the latest version.
How to extract version hints:
- Grep the properties file for the node:
grep 'n8n-nodes-base.set' references/node-registry-properties.jsonl - Search the JSON for all
"@version"values indisplayOptions - Collect all version numbers — they appear in two forms:
- Explicit lists:
"@version": [3, 3.1, 3.2]— the node supports versions 3, 3.1, and 3.2 - Conditions:
"@version": [{"_cnd": {"gte": 3.1}}]— applies to version ≥ 3.1
- Explicit lists:
- The maximum value across all explicit numbers and condition thresholds approximates the latest minor version
Examples from real nodes:
- Set node —
"@version": [3, 3.1, 3.2]→ latest is at least3.2(actual defaultVersion:3.4) - Agent node —
"@version": [{"_cnd": {"gte": 3.1}}]→ latest is at least3.1(actual:3.1) - Switch node —
"@version": [{"_cnd": {"gte": 3.3}}]→ latest is at least3.3(actual:3.4)
Limitations: This technique only provides a lower bound (the actual defaultVersion may be higher). ~458 of 554 nodes have no @version hints at all. The registry cache version field (updated via refresh-node-registry.sh which extracts real defaultVersion from npm packages) is always the most accurate source.
Refreshing the Cache
If a node can't be found in the cached registries (they may be outdated), run the refresh script to pull the latest data from the live APIs:
bash scripts/refresh-node-registry.sh
This fetches from https://api.n8n.io/api/nodes and https://api.n8n.io/api/community-nodes, installs the n8n-nodes-base and @n8n/n8n-nodes-langchain npm packages to extract accurate defaultVersion values (the API only returns major versions), strips heavy fields, and updates all three cache files. Run this when you suspect the cache is stale or when a user asks for a node that should exist but isn't in the cache.
Community Node Warning
When using a community node, the user must install the npm package in their n8n instance first. Always add a sticky() note to the workflow warning about the required community node package:
const installNote = sticky(
'⚠️ Required Community Node: Install the "@mendable/n8n-nodes-firecrawl" package in your n8n instance (Settings → Community Nodes → Install) before using this workflow.',
[firecrawlNode],
{ color: 5 }
)
Lookup Workflow
- User asks for a specific integration (e.g., "add a Slack node")
- Read
references/node-registry-official.jsonand search for the matching node - If not found in official nodes, read
references/node-registry-community.jsonand search there - If still not found, run
bash scripts/refresh-node-registry.shto update the cache, then search again - Use the exact
nameandversionfrom the registry in yournode()/trigger()call - Grep
references/node-registry-properties.jsonlfor the node name to get its available parameters and configure it correctly - If using a community node, add a
sticky()note listing the required npm package - If no matching node exists anywhere, tell the user — do not invent a type
Quick Reference: Core Utility Nodes
These fundamental utility node type names are always safe to use without a registry lookup. However, always look up the correct version from references/node-registry-official.json — do NOT copy version numbers from code examples, as they may be outdated.
| Type | Description |
|---|---|
n8n-nodes-base.manualTrigger | Manual execution trigger |
n8n-nodes-base.webhook | HTTP webhook trigger |
n8n-nodes-base.scheduleTrigger | Cron/interval trigger |
n8n-nodes-base.httpRequest | Generic HTTP request |
n8n-nodes-base.set | Set/transform data fields (v3.4 uses assignments format — never fields.values or { options: {} }) |
n8n-nodes-base.code | Custom JavaScript/Python code |
n8n-nodes-base.if | Conditional branching |
n8n-nodes-base.switch | Multi-branch routing |
n8n-nodes-base.merge | Merge multiple inputs |
n8n-nodes-base.splitInBatches | Batch processing loop |
n8n-nodes-base.noOp | No operation (passthrough) |
n8n-nodes-base.stickyNote | Canvas annotation |
n8n-nodes-base.respondToWebhook | Respond to webhook |
n8n-nodes-base.filter | Filter items |
n8n-nodes-base.executeWorkflow | Execute sub-workflow |
@n8n/n8n-nodes-langchain.agent | AI Agent |
⚠️ IMPORTANT: Version numbers in code examples throughout this skill are illustrative and may be outdated. The only reliable source for the current version of any node is references/node-registry-official.json (or references/node-registry-community.json for community nodes). Always read the registry cache to get the correct version before using any node, even core utility nodes listed above.
For ANY integration node not in this list (Slack, Gmail, Notion, Airtable, Google Sheets, Postgres, etc.), you MUST look up the correct type and version from the registry cache before using it.
Core Concepts
The SDK provides a fluent, chainable API for building n8n workflows in TypeScript/JavaScript. Instead of hand-crafting JSON, you construct workflows programmatically with full type safety, validation, and testing capabilities.
Key capabilities:
- Create workflows — Fluent builder API with
workflow(),node(),trigger() - Import existing workflows —
workflow.fromJSON(json)loads any n8n workflow JSON - Export to JSON —
.toJSON()produces n8n-compatible workflow JSON - Convert JSON to code —
generateWorkflowCode(json)produces readable SDK code - Convert code to JSON —
parseWorkflowCode(code)parses SDK code back to JSON - Validate —
validateWorkflow()checks structure, connections, expressions, and best practices - Test with pin data —
.generatePinData()creates mock data from output declarations - Build AI workflows — Full support for LangChain subnodes (models, tools, memory, etc.)
Common Workflows
Pattern 1: Create a New Workflow from Scratch
import { workflow, node, trigger, validateWorkflow } from '@n8n/workflow-sdk'
const myTrigger = trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: {} })
const httpNode = node({
type: 'n8n-nodes-base.httpRequest', version: 4.4,
config: {
parameters: { url: 'https://api.example.com/data', method: 'GET' },
output: [{ json: { id: 1, name: 'Example' } }]
}
})
const wf = workflow('my-workflow', 'My Workflow')
.add(myTrigger)
.to(httpNode)
.generatePinData()
const validation = validateWorkflow(wf)
if (validation.valid) {
const json = wf.toJSON() // Ready for n8n
}
Pattern 2: Load Existing JSON, Modify, Test, Export
import { workflow, node, generateWorkflowCode, validateWorkflow } from '@n8n/workflow-sdk'
// 1. Load from JSON (user provides their workflow JSON)
const wf = workflow.fromJSON(existingWorkflowJSON)
// 2. Convert to code to understand it
const code = generateWorkflowCode(existingWorkflowJSON)
// 3. Modify — add a new node
const newNode = node({
type: 'n8n-nodes-base.set', version: 3.4,
config: {
name: 'Transform',
parameters: {
mode: 'manual',
assignments: {
assignments: [
{ name: 'processed', value: true, type: 'boolean' },
{ name: 'label', value: '={{ $json.name }}', type: 'string' }
]
},
includeOtherFields: true,
options: {}
}
}
})
// Rebuild with modifications using the SDK
// 4. Validate
const result = validateWorkflow(wf)
console.log(result.errors, result.warnings)
// 5. Export back to JSON
const updatedJSON = wf.toJSON()
Pattern 3: Convert JSON to Code, Modify, Convert Back
import { generateWorkflowCode, parseWorkflowCode, validateWorkflow } from '@n8n/workflow-sdk'
// JSON → Code (for reading/understanding)
const code = generateWorkflowCode(workflowJSON)
// Modify the code as needed...
// Code → JSON (for importing back to n8n)
const newJSON = parseWorkflowCode(modifiedCode)
// Validate before deploying
const result = validateWorkflow(newJSON)
Pattern 4: Build an AI Agent Workflow
import { workflow, node, trigger, languageModel, tool, memory, fromAi } from '@n8n/workflow-sdk'
const model = languageModel({
type: '@n8n/n8n-nodes-langchain.lmChatOpenAi', version: 1.3,
config: {
parameters: {
model: { __rl: true, mode: 'list', value: 'gpt-4o-mini', cachedResultName: 'gpt-4o-mini' },
options: {}
},
credentials: { openAiApi: { name: 'OpenAI', id: 'cred-123' } }
}
})
const emailTool = tool({
type: '@n8n/n8n-nodes-langchain.toolGmail', version: 1,
config: {
parameters: {
recipient: fromAi('recipient', 'Email recipient address'),
subject: fromAi('subject', 'Email subject line')
}
}
})
const agent = node({
type: '@n8n/n8n-nodes-langchain.agent', version: 3.1,
config: {
name: 'AI Agent',
parameters: {
promptType: 'define',
text: '={{ $json.chatInput }}',
options: { systemMessage: 'You are a helpful AI assistant.' }
},
subnodes: { model, tools: [emailTool], memory: memoryNode }
}
})
const wf = workflow('ai-workflow', 'AI Agent Workflow')
.add(trigger({ type: 'n8n-nodes-base.manualTrigger', version: 1, config: {} }))
.to(agent)
⚠️ Model IDs in examples are illustrative and will become outdated. Always choose the most appropriate current model for the user's use case — do NOT copy model IDs like gpt-4o-mini from these examples. Check the provider's latest model offerings.
SDK API Quick Reference
Factory Functions
| Function | Purpose | Reference |
|---|---|---|
workflow(id, name, options?) | Create workflow builder | references/workflow-building.md |
workflow.fromJSON(json) | Import from n8n JSON | references/workflow-building.md |
node(input) | Create a regular node | references/nodes-and-connections.md |
trigger(input) | Create a trigger node | references/nodes-and-connections.md |
sticky(content, nodes?, config?) | Create a sticky note | references/nodes-and-connections.md |
placeholder(hint) | Create a placeholder value | references/nodes-and-connections.md |
newCredential(name) | Mark credential as new | references/nodes-and-connections.md |
ifElse(config) | Create IF node | references/control-flow.md |
switchCase(config) | Create Switch node | references/control-flow.md |
merge(config) | Create Merge node | references/control-flow.md |
splitInBatches(config) | Create Split In Batches | references/control-flow.md |
nextBatch(sib) | Loop-back for SIB | references/control-flow.md |
Subnode Factories (AI/LangChain)
| Function | Purpose |
|---|---|
languageModel(input) | Language model (OpenAI, Anthropic, etc.) |
memory(input) | Memory (Buffer Window, etc.) |
tool(input) | Tool (Calculator, Code, Gmail, etc.) |
outputParser(input) | Output parser |
embedding(input) / embeddings(input) | Embedding model |
vectorStore(input) | Vector store (Pinecone, Qdrant, etc.) |
retriever(input) | Retriever |
documentLoader(input) | Document loader |
textSplitter(input) | Text splitter |
fromAi(key, description?, type?, defaultValue?) | AI-driven parameter |
Expression Utilities
| Function | Purpose |
|---|---|
serializeExpression(fn) | $ => $.json.name → ={{ $json.name }} |
parseExpression(expr) | ={{ $json.name }} → $json.name |
isExpression(value) | Check if string is an expression |
expr(expression) | Mark string as expression (adds = prefix) |
Code Helpers
| Function | Purpose |
|---|---|
runOnceForAllItems(fn) | Code node: process all items at once |
runOnceForEachItem(fn) | Code node: process one item at a time |
Validation & Testing
| Function | Purpose |
|---|---|
validateWorkflow(wf, options?) | Validate workflow structure |
.generatePinData(options?) | Generate pin/test data from outputs |
.validate(options?) | Validate on the builder directly |
Code Generation
| Function | Purpose |
|---|---|
generateWorkflowCode(json) | JSON → TypeScript SDK code |
parseWorkflowCode(code) | SDK code → JSON |
parseWorkflowCodeToBuilder(code) | SDK code → WorkflowBuilder |
WorkflowBuilder Methods
| Method | Purpose |
|---|---|
.add(node) | Add node to workflow |
.to(target) | Connect current node to target |
.connect(src, srcOut, tgt, tgtIn) | Explicit indexed connection |
.settings(settings) | Set workflow settings |
.getNode(name) | Get node by name |
.validate(options?) | Validate the workflow |
.toJSON() | Export as n8n JSON |
.toFormat(format) | Serialize with plugin |
.generatePinData(options?) | Generate test data |
.regenerateNodeIds() | Regenerate deterministic IDs |
NodeInstance Methods
| Method | Purpose |
|---|---|
.to(target, outputIndex?) | Connect to next node(s) |
.input(index) | Create input target (for Merge) |
.output(index) | Select specific output index |
.onTrue(target) | IF node: true branch |
.onFalse(target) | IF node: false branch |
.onCase(index, target) | Switch node: case branch |
.onError(handler) | Set error handler |
.update(config) | Create updated copy |
.getConnections() | Get declared connections |
Decision Guide
When to Use Each Pattern
Creating a brand-new workflow:
- Look up node types — Read
references/node-registry-official.json(andreferences/node-registry-community.jsonif needed) to get the correcttypeandversionfor every integration node - Design the flow (trigger → processing → output)
- Create nodes with
node()andtrigger()using the real types from the registry - If using community nodes, add
sticky()notes listing required npm packages - Chain with
.add()and.to() - Add output declarations for testing
- Validate with
validateWorkflow() - Export with
.toJSON()
Modifying an existing n8n workflow (JSON):
- Load with
workflow.fromJSON(json) - Optionally convert to code with
generateWorkflowCode(json)to understand it - Build the modified version using the SDK
- Validate the new version
- Export with
.toJSON()
Understanding a complex workflow:
- Convert with
generateWorkflowCode(json)— produces readable TypeScript - Read the generated code to understand the flow
Testing/verifying a workflow:
- Build or load the workflow
- Add
outputdeclarations to nodes (expected output shapes) - Call
.generatePinData()to create test data - Call
validateWorkflow()to check for errors - Review validation errors and warnings
Round-tripping (code ↔ JSON):
- JSON → Code:
generateWorkflowCode(json) - Code → JSON:
parseWorkflowCode(code) - Code → Builder:
parseWorkflowCodeToBuilder(code)(allows validation before JSON export)
Connection Patterns Summary
// Sequential: A → B → C
workflow('id', 'name').add(a).to(b).to(c)
// Fan-out: A → [B, C, D] (parallel branches, output indices 0, 1, 2)
workflow('id', 'name').add(a).to([b, c, d])
// Multi-input (Merge): A → merge.input(0), B → merge.input(1)
workflow('id', 'name')
.add(a).to(merge.input(0))
.add(b).to(merge.input(1))
// Explicit indexed: source output 0 → target input 1
workflow('id', 'name').connect(source, 0, target, 1)
// Output selection: use specific output of a multi-output node
nodeA.output(0).to(nodeB)
nodeA.output(1).to(nodeC)
// IF branching
ifNode.onTrue(trueNode).onFalse(falseNode)
// Switch branching
switchNode.onCase(0, caseA).onCase(1, caseB).onCase(2, caseC)
// Error handling
sourceNode.onError(errorHandler)
// Split in Batches loop
sib.onEachBatch(process.to(nextBatch(sib))).onDone(finalNode)
Validation Error Codes
The SDK validates for 23+ error conditions. Key ones to watch for:
| Code | Meaning |
|---|---|
NO_NODES | Workflow has no nodes |
MISSING_TRIGGER | No trigger node found |
DISCONNECTED_NODE | Node not connected to flow |
INVALID_CONNECTION | Invalid connection between nodes |
MISSING_PARAMETER | Required parameter missing |
CIRCULAR_REFERENCE | Circular connection detected |
INVALID_EXPRESSION | Malformed expression |
AGENT_STATIC_PROMPT | AI agent has static prompt |
HARDCODED_CREDENTIALS | Credentials hardcoded in parameters |
MERGE_SINGLE_INPUT | Merge node has only one input |
TOOL_NO_PARAMETERS | Tool has no AI-driven parameters |
FROM_AI_IN_NON_TOOL | fromAi() used outside a tool node |
SUBNODE_NOT_CONNECTED | Subnode not connected to parent |
See references/validation-and-testing.md for the complete list and resolution strategies.
Node Type Constants
import { NODE_TYPES } from '@n8n/workflow-sdk'
NODE_TYPES.IF // 'n8n-nodes-base.if'
NODE_TYPES.SWITCH // 'n8n-nodes-base.switch'
NODE_TYPES.MERGE // 'n8n-nodes-base.merge'
NODE_TYPES.STICKY_NOTE // 'n8n-nodes-base.stickyNote'
NODE_TYPES.SPLIT_IN_BATCHES // 'n8n-nodes-base.splitInBatches'
NODE_TYPES.HTTP_REQUEST // 'n8n-nodes-base.httpRequest'
NODE_TYPES.WEBHOOK // 'n8n-nodes-base.webhook'
NODE_TYPES.DATA_TABLE // 'n8n-nodes-base.dataTable'
Type Guards
import {
isIfNodeType, isSwitchNodeType, isMergeNodeType,
isStickyNoteType, isSplitInBatchesType, isHttpRequestType,
isWebhookType, isDataTableType, isNodeChain, isNodeInstance
} from '@n8n/workflow-sdk'
Scope
This skill is for building and manipulating n8n workflows programmatically using the @n8n/workflow-sdk. It covers workflow creation, JSON import/export, validation, code generation, pin data, and the full SDK API.
This skill is NOT for:
- Deploying workflows to n8n instances or managing n8n server configuration
- Building custom n8n community nodes (use the
n8n-node-builderskill instead) - Using the n8n UI or REST API directly
- Managing n8n credentials in a running instance
Best Practices
- NEVER guess node types or versions — always look them up — Read
references/node-registry-official.json(official) orreferences/node-registry-community.json(community) to get the realtypeandversion. Do NOT copy version numbers from code examples in this skill — they are illustrative and may be outdated. The registry cache is the single source of truth for versions. A wrong node type or version produces a broken workflow. - Flag community nodes with sticky notes — If the workflow uses community nodes, add a
sticky()note listing the npm packages the user must install in their n8n instance. - Always validate before exporting — Call
validateWorkflow()before.toJSON(). The SDK catches 23+ error conditions that would silently produce broken workflows in n8n, so skipping validation means shipping bugs. - Use output declarations for testing — Add
outputto node configs, then callgeneratePinData(). Without output declarations, there's no way to generate test fixtures automatically, and downstream nodes can't be tested with realistic data shapes. - Use
generateWorkflowCode()to understand existing workflows — Raw n8n JSON is verbose and hard to follow. The generated TypeScript code reveals the logical flow, branching, and node relationships at a glance. - Use
parseWorkflowCode()for round-tripping — Code → JSON → Code round-trips cleanly, making it safe to convert, edit in code, and convert back without data loss. - Use
parseWorkflowCodeToBuilder()when you need validation — UnlikeparseWorkflowCode()which returns raw JSON, this returns a WorkflowBuilder so you can.validate()before.toJSON(), catching errors before they reach n8n. - Use credentials objects, not hardcoded values — Hardcoded API keys in parameters are a security risk and won't work across environments. The validator catches
HARDCODED_CREDENTIALSto prevent this. - Use
fromAi()only in tool nodes —fromAi()generates$fromAIexpressions that only the AI Agent node can resolve. Using them in regular nodes produces expressions that will fail at runtime. The validator catchesFROM_AI_IN_NON_TOOL. - Use
expr()for dynamic values — Raw strings with={{ }}are error-prone (missing=prefix, unbalanced braces).expr()handles the prefix automatically and makes intent clear in code. - Set
outputon nodes to enable pin data generation — This is how you create test fixtures. Without declared outputs,generatePinData()has nothing to convert. - Use
sticky()for workflow documentation — Auto-positions around given nodes, making it easy to annotate sections of complex workflows for other developers. - NEVER generate empty Set nodes — A Set node with
parameters: { options: {} }is broken and does nothing. Every Set node v3.4 MUST havemode: 'manual'withassignments.assignmentscontaining at least one entry, ORmode: 'raw'with ajsonOutputstring. Usetype: 'string'/'number'/'boolean'and a singlevaluefield — NOT the oldstringValue/numberValueformat. If you don't know what fields to set, ask the user. - Use current AI models, not example model IDs — Model IDs in code examples (e.g.,
gpt-4o-mini,claude-sonnet-4-20250514) are illustrative and become outdated. Always select the most appropriate current model for the user's task. Check the provider's latest offerings rather than copying from examples.