diff --git a/src/modules/autominator/workflows/_shared/n8n-helpers.md b/src/modules/autominator/workflows/_shared/n8n-helpers.md index d46aa1d6..195915a0 100644 --- a/src/modules/autominator/workflows/_shared/n8n-helpers.md +++ b/src/modules/autominator/workflows/_shared/n8n-helpers.md @@ -1,21 +1,51 @@ # n8n Workflow Helpers +## UUID Generation + +n8n uses UUIDs for node IDs, workflow IDs, and webhook IDs. Generate UUIDs in this format: + +**Full UUID (36 characters):** `f8b7ff4f-6375-4c79-9b2c-9814bfdd0c92` + +- Used for: node `id`, `webhookId`, `versionId` +- Format: 8-4-4-4-12 hexadecimal characters with hyphens + +**Short ID (16 characters):** `Wvmqb0POKmqwCoKy` + +- Used for: workflow `id`, tag `id` +- Format: alphanumeric (a-z, A-Z, 0-9) + +**Assignment ID:** `id-1`, `id-2`, `id-3` + +- Used for: Set node assignments, IF node conditions +- Format: "id-" + sequential number + ## Node Creation Guidelines -### Basic Node Structure +### Basic Node Structure (Modern n8n Format) ```json { - "id": "unique-node-id", + "parameters": {}, + "id": "f8b7ff4f-6375-4c79-9b2c-9814bfdd0c92", "name": "Node Name", "type": "n8n-nodes-base.nodeName", - "typeVersion": 1, - "position": [x, y], - "parameters": {}, + "typeVersion": 2, + "position": [1424, 496], + "webhookId": "b5f0b784-2440-4371-bcf1-b59dd2b29e68", "credentials": {} } ``` +**Critical Rules:** + +- `parameters` comes FIRST +- `id` must be UUID format (e.g., "f8b7ff4f-6375-4c79-9b2c-9814bfdd0c92") +- `type` must be `n8n-nodes-base.nodeName` format (NOT @n8n/n8n-nodes-\*) +- `typeVersion` must be INTEGER (e.g., 2, 3, 4) NOT float (2.1, 3.4) +- `position` must be array of integers: [x, y] +- `webhookId` required for webhook nodes (UUID format) +- Field order matters for n8n compatibility + ### Node Positioning - Start node: [250, 300] @@ -25,12 +55,30 @@ ### Common Node Types +### ⚠️ CRITICAL: Node Type Format Rules + +**ALWAYS use format:** `n8n-nodes-base.nodeName` + +**NEVER use these formats:** + +- ❌ `@n8n/n8n-nodes-slack.slackTrigger` (wrong package format) +- ❌ `n8n-nodes-slack.slackTrigger` (missing base) +- ❌ `slackTrigger` (missing prefix) + +**Correct Examples:** + +- ✅ `n8n-nodes-base.webhook` +- ✅ `n8n-nodes-base.slackTrigger` +- ✅ `n8n-nodes-base.gmail` +- ✅ `n8n-nodes-base.if` + **Trigger Nodes:** - `n8n-nodes-base.webhook` - HTTP webhook trigger - `n8n-nodes-base.scheduleTrigger` - Cron/interval trigger - `n8n-nodes-base.manualTrigger` - Manual execution trigger - `n8n-nodes-base.emailTrigger` - Email trigger +- `n8n-nodes-base.slackTrigger` - Slack event trigger **Action Nodes:** @@ -44,7 +92,8 @@ **Integration Nodes:** - `n8n-nodes-base.googleSheets` - Google Sheets -- `n8n-nodes-base.slack` - Slack +- `n8n-nodes-base.slack` - Slack actions +- `n8n-nodes-base.gmail` - Gmail - `n8n-nodes-base.notion` - Notion - `n8n-nodes-base.airtable` - Airtable - `n8n-nodes-base.postgres` - PostgreSQL @@ -54,21 +103,56 @@ ### Connection Structure +### ⚠️ CRITICAL: Connection Format Rules + +**CORRECT Format:** + ```json { - "node": "Source Node Name", - "type": "main", - "index": 0 + "Source Node Name": { + "main": [ + [ + { + "node": "Target Node Name", + "type": "main", + "index": 0 + } + ] + ] + } +} +``` + +**WRONG Formats:** + +```json +// ❌ WRONG - Missing "main" wrapper +{ + "Source Node Name": [ + [ + { + "node": "Target Node Name", + "type": "main", + "index": 0 + } + ] + ] +} + +// ❌ WRONG - Direct array +{ + "Source Node Name": [[{...}]] } ``` ### Connection Rules 1. Each connection has a source node and target node -2. Main connections use type: "main" -3. Index 0 is default output, index 1+ for conditional branches -4. IF nodes have index 0 (true) and index 1 (false) -5. Always validate that referenced node names exist +2. Connections object structure: `{"Source": {"main": [[{...}]]}}` +3. The "main" key is REQUIRED (wraps the connection array) +4. Index 0 is default output, index 1+ for conditional branches +5. IF nodes have index 0 (true) and index 1 (false) +6. Always validate that referenced node names exist ### Connection Patterns @@ -116,26 +200,124 @@ Trigger → Split → [Branch1, Branch2, Branch3] → Merge ## Data Transformation Patterns -### Using Set Node +### Using Set Node (Modern Format - typeVersion 3+) ```json { "name": "Transform Data", "type": "n8n-nodes-base.set", + "typeVersion": 3, "parameters": { - "mode": "manual", - "values": { - "string": [ + "assignments": { + "assignments": [ { + "id": "id-1", "name": "outputField", - "value": "={{ $json.inputField }}" + "value": "={{ $json.inputField }}", + "type": "string" } ] + }, + "includeOtherFields": true, + "options": {} + } +} +``` + +**Critical Rules for Set Node:** + +- Use `assignments.assignments` structure (not `values`) +- Each assignment needs `id` field (e.g., "id-1", "id-2") +- Each assignment needs `type` field ("string", "number", "boolean") +- Include `includeOtherFields: true` to pass through other data +- Include `options: {}` for compatibility + +### Using Gmail Node (typeVersion 2+) + +```json +{ + "name": "Send Email", + "type": "n8n-nodes-base.gmail", + "typeVersion": 2, + "parameters": { + "sendTo": "user@example.com", + "subject": "Email Subject", + "message": "Email body content", + "options": {} + } +} +``` + +**Critical Rules for Gmail Node:** + +- Use `message` parameter (NOT `text`) +- Use `sendTo` (NOT `to`) +- Include `options: {}` for compatibility + +### Using Slack Node with Channel Selection + +```json +{ + "name": "Slack Action", + "type": "n8n-nodes-base.slack", + "typeVersion": 2, + "parameters": { + "channel": { + "__rl": true, + "value": "general", + "mode": "list", + "cachedResultName": "#general" } } } ``` +**Critical Rules for Slack Channel:** + +- Use `__rl: true` flag for resource locator +- Include `mode: "list"` for channel selection +- Include `cachedResultName` with # prefix + +### Using IF Node (typeVersion 2+) + +```json +{ + "name": "Check Condition", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "parameters": { + "conditions": { + "options": { + "caseSensitive": false, + "leftValue": "", + "typeValidation": "loose" + }, + "conditions": [ + { + "id": "id-1", + "leftValue": "={{ $json.field }}", + "rightValue": "value", + "operator": { + "type": "string", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "options": {} + } +} +``` + +**Critical Rules for IF Node:** + +- Use `conditions.conditions` structure +- Each condition needs `id` field +- Do NOT include `name` field in conditions +- Use `operator` object with `type` and `operation` +- Include `options` at root level + ### Using Code Node ```json @@ -171,7 +353,7 @@ Trigger → Split → [Branch1, Branch2, Branch3] → Merge - `httpHeaderAuth` - Header-based auth - `httpQueryAuth` - Query parameter auth -## Workflow Metadata +## Workflow Metadata (Modern n8n Format) ### Required Fields @@ -179,15 +361,37 @@ Trigger → Split → [Branch1, Branch2, Branch3] → Merge { "name": "Workflow Name", "nodes": [], + "pinData": {}, "connections": {}, "active": false, "settings": { "executionOrder": "v1" }, - "tags": [] + "versionId": "7d745171-e378-411c-bd0a-25a8368a1cb6", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "2229c21690ffe7e7b16788a579be3103980c4445acb933f7ced2a6a17f0bd18b" + }, + "id": "Wvmqb0POKmqwCoKy", + "tags": [ + { + "name": "Automation", + "id": "7FHIZPUaIaChwuiS", + "updatedAt": "2025-11-21T19:39:46.484Z", + "createdAt": "2025-11-21T19:39:46.484Z" + } + ] } ``` +**Critical Rules:** + +- `pinData` must be empty object `{}` +- `versionId` must be UUID +- `meta` object with `templateCredsSetupCompleted` and `instanceId` +- `id` must be short alphanumeric (e.g., "Wvmqb0POKmqwCoKy") +- `tags` must be array of objects (not strings) with id, name, createdAt, updatedAt + ## Validation Checklist - [ ] All node IDs are unique