n8n-syntax-workflow-json — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited n8n-syntax-workflow-json (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
| Field | Type | Required | Purpose | |
|---|---|---|---|---|
id | string | YES | Unique workflow identifier | |
name | string | YES | Human-readable workflow name | |
active | boolean | YES | Whether workflow listens for triggers | |
isArchived | boolean | YES | Whether workflow is archived | |
nodes | INode[] | YES | Array of all nodes in the workflow | |
connections | IConnections | YES | Connection map between nodes | |
settings | IWorkflowSettings | NO | Workflow-level configuration | |
staticData | IDataObject | NO | Persistent data (polling state, etc.) | |
pinData | IPinData | NO | Pinned test data for manual execution | |
description | `string \ | null` | NO | Workflow description |
versionId | string | NO | Version identifier | |
meta | WorkflowFEMeta | NO | Frontend metadata (template ID, etc.) |
| Field | Type | Required | Purpose |
|---|---|---|---|
id | string | YES | Unique node ID (UUID format) |
name | string | YES | Display name (MUST be unique in workflow) |
type | string | YES | Node type (e.g., n8n-nodes-base.httpRequest) |
typeVersion | number | YES | Node version number |
position | [number, number] | YES | Canvas position [x, y] |
parameters | INodeParameters | YES | Node parameter values |
credentials | INodeCredentials | NO | Credential references |
disabled | boolean | NO | Whether node is disabled |
notes | string | NO | User-visible notes |
notesInFlow | boolean | NO | Show notes on canvas |
webhookId | string | NO | Webhook ID (webhook nodes only) |
retryOnFail | boolean | NO | Retry on error |
maxTries | number | NO | Max retry attempts |
waitBetweenTries | number | NO | Milliseconds between retries |
alwaysOutputData | boolean | NO | Output empty item if no data |
executeOnce | boolean | NO | Execute only for first item |
onError | OnError | NO | Error behavior strategy |
continueOnFail | boolean | NO | Continue workflow on error |
| Value | Behavior |
|---|---|
continueErrorOutput | Route to error output |
continueRegularOutput | Route to regular output with error info |
stopWorkflow | Stop entire workflow execution |
| Constant | String Value | Purpose |
|---|---|---|
NodeConnectionTypes.Main | "main" | Standard data flow |
NodeConnectionTypes.AiAgent | "ai_agent" | AI agent connection |
NodeConnectionTypes.AiChain | "ai_chain" | AI chain connection |
NodeConnectionTypes.AiDocument | "ai_document" | AI document loader |
NodeConnectionTypes.AiEmbedding | "ai_embedding" | AI embedding model |
NodeConnectionTypes.AiLanguageModel | "ai_languageModel" | AI language model |
NodeConnectionTypes.AiMemory | "ai_memory" | AI memory store |
NodeConnectionTypes.AiOutputParser | "ai_outputParser" | AI output parser |
NodeConnectionTypes.AiRetriever | "ai_retriever" | AI retriever |
NodeConnectionTypes.AiReranker | "ai_reranker" | AI reranker |
NodeConnectionTypes.AiTextSplitter | "ai_textSplitter" | AI text splitter |
NodeConnectionTypes.AiTool | "ai_tool" | AI tool |
NodeConnectionTypes.AiVectorStore | "ai_vectorStore" | AI vector store |
NEVER use node id values as connection keys -- connections are keyed by node name, not ID. Using IDs silently produces broken workflows.
NEVER omit the type field in connection targets -- every IConnection object MUST have node, type, AND index. Missing type causes runtime errors.
NEVER duplicate node names within a workflow -- each name field MUST be unique. n8n uses names as connection keys, so duplicates break the connection map.
ALWAYS use the 3-level nesting for connections: sourceName → connectionType → outputIndex → targets[]. Flat or 2-level structures are invalid.
ALWAYS set typeVersion to a valid version for the node type. Omitting or using an unsupported version causes the node to fail silently or use unexpected defaults.
This is the #1 source of errors when generating workflow JSON. The connection format uses three nesting levels:
Level 1: Source node NAME (string key)
Level 2: Connection TYPE (string key, usually "main")
Level 3: Output INDEX (array position)
→ Array of target connections [{node, type, index}]// Level 1: Keyed by SOURCE node name
interface IConnections {
[sourceNodeName: string]: INodeConnections;
}
// Level 2: Keyed by connection type
interface INodeConnections {
[connectionType: string]: NodeInputConnections;
}
// Level 3: Array indexed by output index
type NodeInputConnections = Array<IConnection[] | null>;
// Individual connection target
interface IConnection {
node: string; // Destination node NAME
type: NodeConnectionType; // Connection type at destination
index: number; // Destination INPUT index
}{
"connections": {
"HTTP Request": { // Level 1: source node name
"main": [ // Level 2: connection type
[ // Level 3: output index 0
{
"node": "Set", // target node name
"type": "main", // target connection type
"index": 0 // target input index
}
]
]
}
}
}{
"IF": {
"main": [
[{"node": "True Handler", "type": "main", "index": 0}],
[{"node": "False Handler", "type": "main", "index": 0}]
]
}
}0 = true branch → routes to "True Handler"1 = false branch → routes to "False Handler"{
"Trigger": {
"main": [
[
{"node": "Branch A", "type": "main", "index": 0},
{"node": "Branch B", "type": "main", "index": 0}
]
]
}
}Multiple targets in the SAME output index array means parallel fan-out.
AI nodes use typed connections instead of "main":
{
"OpenAI Chat Model": {
"ai_languageModel": [
[{"node": "AI Agent", "type": "ai_languageModel", "index": 0}]
]
},
"Calculator Tool": {
"ai_tool": [
[{"node": "AI Agent", "type": "ai_tool", "index": 0}]
]
},
"Window Buffer Memory": {
"ai_memory": [
[{"node": "AI Agent", "type": "ai_memory", "index": 0}]
]
}
}interface IWorkflowSettings {
timezone?: 'DEFAULT' | string; // e.g., "Europe/Amsterdam"
errorWorkflow?: 'DEFAULT' | string; // Workflow ID to run on error
callerIds?: string; // Allowed caller workflow IDs
callerPolicy?: CallerPolicy; // Sub-workflow access
saveDataErrorExecution?: SaveDataExecution; // "all" | "none" | "DEFAULT"
saveDataSuccessExecution?: SaveDataExecution;
saveManualExecutions?: 'DEFAULT' | boolean;
saveExecutionProgress?: 'DEFAULT' | boolean;
executionTimeout?: number; // Timeout in seconds
executionOrder?: 'v0' | 'v1'; // ALWAYS use 'v1' for new workflows
}ALWAYS set executionOrder to "v1" for new workflows. The "v0" algorithm has known edge cases with complex branching.
START: Creating workflow JSON
├── Define all nodes first
│ ├── Each node MUST have: id, name, type, typeVersion, position, parameters
│ ├── Each name MUST be unique across the workflow
│ └── Use UUID format for id fields
├── Build connections after nodes
│ ├── Is this a standard data flow?
│ │ └── YES → Use "main" as connection type
│ ├── Is this an AI sub-node connection?
│ │ └── YES → Use the specific ai_* connection type
│ ├── Does the source node have multiple outputs?
│ │ └── YES → Add entries at each output index position
│ └── Does one output go to multiple nodes?
│ └── YES → Add multiple targets in the same output index array
└── Add settings
└── ALWAYS include executionOrder: "v1"~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.