Skill: kassimisai/n8n-builder (version 1.0.0)

--- name: n8n-builder description: Expert n8n workflow builder that creates, deploys, and manages n8n workflows programmatically via the n8n REST API. Use when asked to create n8n workflows, automate n8n tasks, build automations, design workflow pipelines, connect services via n8n, or manage existing n8n workflows. Handles webhook flows, scheduled tasks, AI agents, database syncs, conditional logic, error handling, and any n8n node configuration. --- # n8n Workflow Builder ## Setup Requires two environment variables: - `N8N_URL` — n8n instance URL (e.g. `https://your-n8n.example.com`) - `N8N_API_KEY` — n8n API key (Settings → API → Create API Key) ## Workflow 1. **Understand the automation** — Clarify trigger (webhook/schedule/manual), data sources, processing logic, outputs, and error handling needs. 2. **Design the workflow JSON** — Build valid n8n workflow JSON following the schema in `references/workflow-schema.md`. Use patterns from `references/workflow-patterns.md` as templates. 3. **Deploy via API** — Use `scripts/n8n-api.sh create <file>` or pipe JSON to `scripts/n8n-api.sh create-stdin`. 4. **Activate** — Use `scripts/n8n-api.sh activate <workflow_id>` for trigger-based workflows. 5. **Verify** — List workflows to confirm deployment: `scripts/n8n-api.sh list`. ## API Script Reference ```bash # List all workflows scripts/n8n-api.sh list # Create workflow from JSON file scripts/n8n-api.sh create /tmp/workflow.json # Create from stdin echo '{"name":"Test",...}' | scripts/n8n-api.sh create-stdin # Get, activate, deactivate, delete, execute scripts/n8n-api.sh get <id> scripts/n8n-api.sh activate <id> scripts/n8n-api.sh deactivate <id> scripts/n8n-api.sh delete <id> scripts/n8n-api.sh execute <id> # List credentials and tags scripts/n8n-api.sh credentials scripts/n8n-api.sh tags ``` ## Building Workflow JSON Every workflow needs: `name`, `nodes[]`, `connections{}`, `settings{}`. Every node needs: `id`, `name`, `type`, `typeVersion`, `position`, `parameters`. Connections use **source node display name** as key, mapping outputs to target nodes. For full schema, node types, and expression syntax → read `references/workflow-schema.md` For complete workflow examples (webhook, schedule, AI agent, DB sync, error handling) → read `references/workflow-patterns.md` ## Key Rules - **Always set `"executionOrder": "v1"`** in settings - **Node names must be unique** within a workflow - **Node IDs must be unique** — use descriptive slugs like `webhook1`, `code1` - **Position nodes** starting at `[250, 300]`, spacing ~200px horizontally - **IF nodes** have two outputs: index 0 = true, index 1 = false - **Webhook workflows** need `respondToWebhook` node if `responseMode` is `responseNode` - **Credentials** must exist in n8n before activation — check with `scripts/n8n-api.sh credentials` - **Test before activating** — use `scripts/n8n-api.sh execute <id>` for manual trigger workflows - **Use `continueOnFail: true`** on risky HTTP/API nodes, then check for errors downstream ## Common Real Estate Workflows - **Lead intake**: Webhook → validate → dedupe → insert DB → notify Slack/SMS - **Call follow-up**: Schedule → query DB for completed calls → send SMS/email based on outcome - **Drip campaign**: Schedule → query leads by stage → send stage-appropriate email/SMS - **CRM sync**: Webhook → transform → update HubSpot/Salesforce + internal DB - **Property alerts**: Schedule → scrape/API listings → filter new → notify leads - **AI qualification**: Webhook → AI Agent (classify lead intent) → route to appropriate pipeline

path: references/workflow-patterns.md
content: # n8n Workflow Patterns

## Pattern 1: Webhook → Process → Respond

Receive HTTP request, process data, return response.

```json
{
  "name": "Webhook Processor",
  "nodes": [
    {
      "id": "webhook1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [250, 300],
      "parameters": {
        "path": "my-endpoint",
        "httpMethod": "POST",
        "responseMode": "responseNode"
      }
    },
    {
      "id": "code1",
      "name": "Process",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [450, 300],
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  item.json.processed = true;\n}\nreturn $input.all();"
      }
    },
    {
      "id": "respond1",
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [650, 300],
      "parameters": {
        "respondWith": "allIncomingItems"
      }
    }
  ],
  "connections": {
    "Webhook": { "main": [[ { "node": "Process", "type": "main", "index": 0 } ]] },
    "Process": { "main": [[ { "node": "Respond", "type": "main", "index": 0 } ]] }
  },
  "settings": { "executionOrder": "v1" }
}
```

## Pattern 2: Schedule → Fetch → Conditional → Action

Periodic check with branching logic.

```json
{
  "name": "Scheduled Check",
  "nodes": [
    {
      "id": "schedule1",
      "name": "Every Hour",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [250, 300],
      "parameters": {
        "rule": { "interval": [{ "field": "hours", "hoursInterval": 1 }] }
      }
    },
    {
      "id": "http1",
      "name": "Fetch Data",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [450, 300],
      "parameters": {
        "url": "https://api.example.com/data",
        "method": "GET"
      }
    },
    {
      "id": "if1",
      "name": "Has Results?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [650, 300],
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" },
          "conditions": [
            { "leftValue": "={{ $json.results.length }}", "rightValue": "0", "operator": { "type": "number", "operation": "gt" } }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "slack1",
      "name": "Notify Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [850, 200],
      "parameters": {
        "resource": "message",
        "operation": "post",
        "channel": "#alerts",
        "text": "={{ $json.results.length }} new results found"
      }
    },
    {
      "id": "noop1",
      "name": "No Results",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [850, 400],
      "parameters": {}
    }
  ],
  "connections": {
    "Every Hour": { "main": [[ { "node": "Fetch Data", "type": "main", "index": 0 } ]] },
    "Fetch Data": { "main": [[ { "node": "Has Results?", "type": "main", "index": 0 } ]] },
    "Has Results?": {
      "main": [
        [ { "node": "Notify Slack", "type": "main", "index": 0 } ],
        [ { "node": "No Results", "type": "main", "index": 0 } ]
      ]
    }
  },
  "settings": { "executionOrder": "v1" }
}
```

## Pattern 3: AI Agent with Tools

LangChain AI agent that can call sub-workflows as tools.

```json
{
  "name": "AI Agent",
  "nodes": [
    {
      "id": "webhook1",
      "name": "Chat Input",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [250, 300],
      "parameters": {
        "path": "chat",
        "httpMethod": "POST",
        "responseMode": "responseNode"
      }
    },
    {
      "id": "agent1",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.7,
      "position": [550, 300],
      "parameters": {
        "text": "={{ $json.body.message }}",
        "options": { "systemMessage": "You are a helpful assistant." }
      }
    },
    {
      "id": "llm1",
      "name": "OpenAI",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1,
      "position": [550, 500],
      "parameters": {
        "model": "gpt-4o",
        "options": { "temperature": 0.7 }
      }
    },
    {
      "id": "respond1",
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [750, 300],
      "parameters": { "respondWith": "allIncomingItems" }
    }
  ],
  "connections": {
    "Chat Input": { "main": [[ { "node": "AI Agent", "type": "main", "index": 0 } ]] },
    "AI Agent": { "main": [[ { "node": "Respond", "type": "main", "index": 0 } ]] },
    "OpenAI": { "ai_languageModel": [[ { "node": "AI Agent", "type": "ai_languageModel", "index": 0 } ]] }
  },
  "settings": { "executionOrder": "v1" }
}
```

## Pattern 4: Database → Transform → Multi-Output

Read from DB, transform data, send to multiple destinations.

```json
{
  "name": "DB Sync",
  "nodes": [
    {
      "id": "trigger1",
      "name": "Every Day",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [250, 300],
      "parameters": {
        "rule": { "interval": [{ "field": "days", "daysInterval": 1 }] }
      }
    },
    {
      "id": "pg1",
      "name": "Query DB",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [450, 300],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT * FROM leads WHERE status = 'new' AND created_at > NOW() - INTERVAL '1 day'"
      }
    },
    {
      "id": "set1",
      "name": "Transform",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [650, 300],
      "parameters": {
        "mode": "manual",
        "fields": {
          "values": [
            { "name": "fullName", "stringValue": "={{ $json.first_name }} {{ $json.last_name }}" },
            { "name": "phone", "stringValue": "={{ $json.phone }}" }
          ]
        }
      }
    },
    {
      "id": "sheets1",
      "name": "Update Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [850, 200],
      "parameters": {
        "operation": "append",
        "documentId": { "__rl": true, "mode": "url", "value": "https://docs.google.com/spreadsheets/d/xxx" },
        "sheetName": { "__rl": true, "mode": "list", "value": "Sheet1" }
      }
    },
    {
      "id": "slack1",
      "name": "Notify",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [850, 400],
      "parameters": {
        "resource": "message",
        "operation": "post",
        "channel": "#leads",
        "text": "={{ $json.fullName }} - new lead added"
      }
    }
  ],
  "connections": {
    "Every Day": { "main": [[ { "node": "Query DB", "type": "main", "index": 0 } ]] },
    "Query DB": { "main": [[ { "node": "Transform", "type": "main", "index": 0 } ]] },
    "Transform": { "main": [[
      { "node": "Update Sheet", "type": "main", "index": 0 },
      { "node": "Notify", "type": "main", "index": 0 }
    ]] }
  },
  "settings": { "executionOrder": "v1" }
}
```

## Pattern 5: Error Handling

Wrap risky operations with error handling.

```json
{
  "nodes": [
    {
      "id": "trigger1",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [250, 300],
      "parameters": {}
    },
    {
      "id": "http1",
      "name": "Risky Call",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [450, 300],
      "parameters": {
        "url": "https://api.example.com/fragile",
        "options": { "timeout": 10000 }
      },
      "continueOnFail": true
    },
    {
      "id": "if1",
      "name": "Check Error",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [650, 300],
      "parameters": {
        "conditions": {
          "conditions": [
            { "leftValue": "={{ $json.error }}", "rightValue": "", "operator": { "type": "string", "operation": "exists" } }
          ],
          "combinator": "and"
        }
      }
    }
  ]
}
```

## Expression Syntax Quick Reference

- Access current item: `{{ $json.fieldName }}`
- Access input item: `{{ $input.item.json.field }}`
- Previous node: `{{ $('Node Name').item.json.field }}`
- All items from node: `{{ $('Node Name').all() }}`
- Environment variable: `{{ $env.MY_VAR }}`
- Execution ID: `{{ $execution.id }}`
- Workflow ID: `{{ $workflow.id }}`
- Current timestamp: `{{ $now.toISO() }}`
- Math: `{{ Math.round($json.price * 1.1) }}`
- Conditional: `{{ $json.status === 'active' ? 'Yes' : 'No' }}`


path: references/workflow-schema.md
content: # n8n Workflow JSON Schema Reference

## Root Structure

```json
{
  "name": "Workflow Name",
  "nodes": [],
  "connections": {},
  "settings": {},
  "active": false
}
```

## Node Object (Required Fields)

```json
{
  "id": "unique-id-string",
  "name": "Display Name",
  "type": "n8n-nodes-base.nodeType",
  "typeVersion": 1,
  "position": [x, y],
  "parameters": {}
}
```

Optional: `credentials`, `disabled`, `notes`, `notesInFlow`

## Connections Object

Connections map source node names to destination nodes:

```json
{
  "Source Node Name": {
    "main": [
      [
        { "node": "Target Node Name", "type": "main", "index": 0 }
      ]
    ]
  }
}
```

- `main` = array of output arrays (index 0 = first output, 1 = second for IF nodes, etc.)
- Each output array contains connection objects pointing to target nodes

### IF Node (two outputs)

```json
{
  "IF": {
    "main": [
      [ { "node": "True Branch", "type": "main", "index": 0 } ],
      [ { "node": "False Branch", "type": "main", "index": 0 } ]
    ]
  }
}
```

## Common Node Types

### Triggers
| Type | Description |
|------|-------------|
| `n8n-nodes-base.manualTrigger` | Manual execution |
| `n8n-nodes-base.webhook` | HTTP webhook (GET/POST/etc.) |
| `n8n-nodes-base.scheduleTrigger` | Cron/interval schedule |
| `n8n-nodes-base.emailTrigger` | IMAP email trigger |
| `n8n-nodes-base.n8nTrigger` | Triggered by another workflow |

### Core Nodes
| Type | Description |
|------|-------------|
| `n8n-nodes-base.set` | Set/transform values |
| `n8n-nodes-base.code` | 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` | Process items in batches |
| `n8n-nodes-base.wait` | Delay/wait |
| `n8n-nodes-base.noOp` | No operation (passthrough) |
| `n8n-nodes-base.respondToWebhook` | Return webhook response |
| `n8n-nodes-base.executeWorkflow` | Call another workflow |
| `n8n-nodes-base.function` | Legacy code node |
| `n8n-nodes-base.filter` | Filter items by condition |
| `n8n-nodes-base.removeDuplicates` | Deduplicate items |
| `n8n-nodes-base.sort` | Sort items |
| `n8n-nodes-base.limit` | Limit number of items |
| `n8n-nodes-base.aggregate` | Aggregate items |
| `n8n-nodes-base.itemLists` | Split/concat/limit items |
| `n8n-nodes-base.dateTime` | Date/time operations |
| `n8n-nodes-base.crypto` | Hash, encrypt, etc. |
| `n8n-nodes-base.xml` | XML parse/generate |
| `n8n-nodes-base.html` | HTML extract/generate |
| `n8n-nodes-base.markdown` | Markdown convert |
| `n8n-nodes-base.errorTrigger` | Handle workflow errors |

### HTTP & API
| Type | Description |
|------|-------------|
| `n8n-nodes-base.httpRequest` | Generic HTTP request |
| `n8n-nodes-base.graphql` | GraphQL queries |
| `n8n-nodes-base.ftp` | FTP upload/download |
| `n8n-nodes-base.ssh` | SSH commands |

### Databases
| Type | Description |
|------|-------------|
| `n8n-nodes-base.postgres` | PostgreSQL |
| `n8n-nodes-base.mysql` | MySQL |
| `n8n-nodes-base.mongoDb` | MongoDB |
| `n8n-nodes-base.redis` | Redis |
| `n8n-nodes-base.sqlite` | SQLite |

### Communication
| Type | Description |
|------|-------------|
| `n8n-nodes-base.slack` | Slack |
| `n8n-nodes-base.discord` | Discord |
| `n8n-nodes-base.telegram` | Telegram |
| `n8n-nodes-base.twilio` | Twilio SMS/call |
| `n8n-nodes-base.sendGrid` | SendGrid email |
| `n8n-nodes-base.gmail` | Gmail |
| `n8n-nodes-base.microsoftOutlook` | Outlook |

### CRM & Sales
| Type | Description |
|------|-------------|
| `n8n-nodes-base.hubspot` | HubSpot |
| `n8n-nodes-base.salesforce` | Salesforce |
| `n8n-nodes-base.pipedrive` | Pipedrive |
| `n8n-nodes-base.airtable` | Airtable |

### AI Nodes
| Type | Description |
|------|-------------|
| `@n8n/n8n-nodes-langchain.openAi` | OpenAI API |
| `@n8n/n8n-nodes-langchain.agent` | AI Agent (LangChain) |
| `@n8n/n8n-nodes-langchain.chainLlm` | LLM Chain |
| `@n8n/n8n-nodes-langchain.chainSummarization` | Summarization Chain |
| `@n8n/n8n-nodes-langchain.toolWorkflow` | Tool (sub-workflow) |
| `@n8n/n8n-nodes-langchain.memoryBufferWindow` | Conversation memory |
| `@n8n/n8n-nodes-langchain.outputParserStructured` | Structured output |

### Files & Storage
| Type | Description |
|------|-------------|
| `n8n-nodes-base.googleSheets` | Google Sheets |
| `n8n-nodes-base.googleDrive` | Google Drive |
| `n8n-nodes-base.s3` | AWS S3 |
| `n8n-nodes-base.readWriteFile` | Local file read/write |
| `n8n-nodes-base.spreadsheetFile` | CSV/XLS processing |

### Other
| Type | Description |
|------|-------------|
| `n8n-nodes-base.googleCalendar` | Google Calendar |
| `n8n-nodes-base.stripe` | Stripe payments |
| `n8n-nodes-base.shopify` | Shopify |
| `n8n-nodes-base.notion` | Notion |
| `n8n-nodes-base.github` | GitHub |
| `n8n-nodes-base.jira` | Jira |

## Node Positioning Guidelines

- Start trigger at `[250, 300]`
- Space nodes ~200px apart horizontally
- Keep vertically aligned for linear flows
- Branch nodes offset vertically by ±150px

## Settings Object

```json
{
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner"
  }
}
```

## Credentials Reference

Credentials are referenced by name and type in nodes:

```json
{
  "credentials": {
    "twilioApi": {
      "id": "cred_id",
      "name": "My Twilio"
    }
  }
}
```

Credentials must exist in the target n8n instance before workflow activation. The API cannot create credentials — set them up in the n8n UI first.


path: scripts/n8n-api.sh
content: #!/usr/bin/env bash
# n8n API helper — create, list, get, activate, deactivate, delete workflows
# Requires: N8N_URL and N8N_API_KEY environment variables
set -euo pipefail

N8N_URL="${N8N_URL:-http://localhost:5678}"
N8N_API_KEY="${N8N_API_KEY:-}"

if [ -z "$N8N_API_KEY" ]; then
  echo "ERROR: N8N_API_KEY not set" >&2; exit 1
fi

AUTH_HEADER="X-N8N-[REDACTED_SECRET]"
BASE="$N8N_URL/api/v1"

usage() {
  cat <<EOF
Usage: n8n-api.sh <command> [args]

Commands:
  list                          List all workflows
  get <workflow_id>             Get workflow by ID
  create <json_file>            Create workflow from JSON file
  create-stdin                  Create workflow from stdin JSON
  update <workflow_id> <json>   Update workflow from JSON file
  activate <workflow_id>        Activate workflow
  deactivate <workflow_id>      Deactivate workflow
  delete <workflow_id>          Delete workflow
  execute <workflow_id>         Execute workflow manually
  tags                          List all tags
  credentials                   List all credentials
EOF
}

case "${1:-}" in
  list)
    curl -s -H "$AUTH_HEADER" "$BASE/workflows" | jq .
    ;;
  get)
    curl -s -H "$AUTH_HEADER" "$BASE/workflows/$2" | jq .
    ;;
  create)
    curl -s -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
      -d @"$2" "$BASE/workflows" | jq .
    ;;
  create-stdin)
    curl -s -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
      -d @- "$BASE/workflows" | jq .
    ;;
  update)
    curl -s -X PUT -H "$AUTH_HEADER" -H "Content-Type: application/json" \
      -d @"$3" "$BASE/workflows/$2" | jq .
    ;;
  activate)
    curl -s -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
      -d '{"active": true}' "$BASE/workflows/$2/activate" | jq .
    ;;
  deactivate)
    curl -s -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
      -d '{"active": false}' "$BASE/workflows/$2/deactivate" | jq .
    ;;
  delete)
    curl -s -X DELETE -H "$AUTH_HEADER" "$BASE/workflows/$2" | jq .
    ;;
  execute)
    curl -s -X POST -H "$AUTH_HEADER" "$BASE/workflows/$2/run" | jq .
    ;;
  tags)
    curl -s -H "$AUTH_HEADER" "$BASE/tags" | jq .
    ;;
  credentials)
    curl -s -H "$AUTH_HEADER" "$BASE/credentials" | jq .
    ;;
  *)
    usage
    ;;
esac


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)