name: N8N Development description: Create and manage N8N workflows in Orchestrator AI. Use Helper LLM pattern for all LLM calls, configure webhook status tracking, handle API responses. CRITICAL: All workflows using Helper LLM must include required parameters (taskId, conversationId, userId, statusWebhook, stepName, sequence, totalSteps). Status webhook URL must read from environment variables. allowed-tools: Read, Write, Edit, Bash, Grep, Glob
N8N Development Skill
CRITICAL: All N8N workflows that use Helper LLM MUST include required parameters. Status webhook URL MUST read from environment variables (never hardcoded).
When to Use This Skill
Use this skill when:
- Creating new N8N workflows
- Calling Helper LLM from workflows
- Configuring webhook status tracking
- Handling API responses from workflows
- Wrapping N8N workflows as API agents
- Setting up workflow parameters
The Helper LLM Pattern
Workflow ID: 9jxl03jCcqg17oOy
Name: "Helper: LLM Task"
This is Orchestrator AI's standard building block for all LLM calls in N8N workflows. It provides:
- Multi-provider support (OpenAI, Anthropic, Ollama)
- Status tracking via webhooks
- Normalized output format
- Error handling
How to Call Helper LLM from a Workflow
Step 1: Extract Parameters from Webhook
When your workflow receives a webhook, extract all required parameters:
{
"taskId": "{{ $json.body.taskId }}",
"conversationId": "{{ $json.body.conversationId }}",
"userId": "{{ $json.body.userId }}",
"statusWebhook": "={{ $json.body.statusWebhook || process.env.API_BASE_URL + '/webhooks/status' }}",
"provider": "={{ $json.body.provider || 'openai' }}",
"model": "={{ $json.body.model || 'gpt-4' }}",
"announcement": "={{ $json.body.announcement }}"
}
Step 2: Prepare Parameters for Helper LLM
Create a "Set" node that prepares all parameters for Helper LLM:
Example from Marketing Swarm workflow (storage/snapshots/n8n/marketing-swarm-flexible-llm.json):
{
"name": "announcement",
"type": "string",
"value": "={{ $json.body.announcement }}",
"id": "37100b7a-3727-4855-824f-2725e80d0440"
},
{
"name": "taskId",
"type": "string",
"value": "={{ $json.body.taskId }}",
"id": "c26c5743-8792-41fc-807a-65cc83a14ca1"
},
{
"name": "conversationId",
"type": "string",
"value": "={{ $json.body.conversationId }}",
"id": "f95fd7fb-df93-4dd3-8450-2830ce517fcd"
},
{
"name": "userId",
"type": "string",
"value": "={{ $json.body.userId }}",
"id": "56763026-b467-4e4b-b3fb-7842b63c1caf"
},
{
"name": "statusWebhook",
"type": "string",
"value": "={{ $json.body.statusWebHook }}",
"id": "5b5c4d3a-93bf-4f2d-aadf-e31b89a41079"
},
{
"id": "a95859db-69f5-46c2-a895-883b3659deac",
"name": "systemMessage",
"value": "You are a social media content strategist. Create engaging social media posts (NOT blog posts) for multiple platforms: Twitter/X (280 chars with hashtags), LinkedIn (professional tone, 1300 chars max), and Facebook (conversational, 500 chars). Focus on hooks, engagement, and platform-specific best practices. Include relevant hashtags and emojis where appropriate.",
"type": "string"
},
{
"id": "5c7b8969-c60a-42df-b9dc-84849e0f10a2",
"name": "userMessage",
"value": "={{ $json.body.announcement }}",
"type": "string"
},
{
"id": "7b8664d1-0f50-4a1d-ad16-3867967041f8",
"name": "stepName",
"value": "Create Social Media",
"type": "string"
},
{
"id": "291d34dd-2292-4cea-9432-3ae16b054053",
"name": "sequence",
"value": "3",
"type": "string"
},
{
"id": "8cc15d2f-cab2-4691-b5ec-954ded016211",
"name": "totalSteps",
"value": "3",
"type": "string"
}
Critical Parameters:
| Parameter | Type | Required | Example | Description |
|---|---|---|---|---|
taskId |
string | ✅ Yes | "uuid" |
Task identifier for tracking |
conversationId |
string | ✅ Yes | "uuid" |
Conversation context |
userId |
string | ✅ Yes | "uuid" |
User identifier |
statusWebhook |
string | ✅ Yes* | "${API_BASE_URL}/webhooks/status" |
Webhook URL (from env) |
stepName |
string | ✅ Yes | "Create Social Media" |
Descriptive step name |
sequence |
number | ✅ Yes | 3 |
Step number (1-based) |
totalSteps |
number | ✅ Yes | 3 |
Total steps in workflow |
userMessage |
string | ✅ Yes | "Write a blog post about..." |
The prompt/message |
systemMessage |
string | ❌ No | "You are an expert..." |
System prompt |
provider |
string | ❌ No | "openai" |
LLM provider |
model |
string | ❌ No | "gpt-4" |
Model name |
temperature |
number | ❌ No | 0.7 |
Temperature (0.0-1.0) |
maxTokens |
number | ❌ No | 1000 |
Max tokens |
Note: statusWebhook is REQUIRED if you want status tracking to work.
Step 3: Call Helper LLM via Execute Workflow Node
Configure the "Execute Workflow" node:
{
"source": "database",
"workflowId": "9jxl03jCcqg17oOy",
"fieldMapping": {
"fields": [
{ "name": "taskId", "value": "={{ $json.taskId }}" },
{ "name": "conversationId", "value": "={{ $json.conversationId }}" },
{ "name": "userId", "value": "={{ $json.userId }}" },
{ "name": "statusWebhook", "value": "={{ $json.statusWebhook }}" },
{ "name": "stepName", "value": "={{ $json.stepName }}" },
{ "name": "sequence", "value": "={{ $json.sequence }}" },
{ "name": "totalSteps", "value": "={{ $json.totalSteps }}" },
{ "name": "userMessage", "value": "={{ $json.userMessage }}" },
{ "name": "systemMessage", "value": "={{ $json.systemMessage }}" },
{ "name": "provider", "value": "={{ $json.provider || 'openai' }}" },
{ "name": "model", "value": "={{ $json.model || 'gpt-4' }}" },
{ "name": "temperature", "value": "={{ $json.temperature || 0.7 }}" },
{ "name": "maxTokens", "value": "={{ $json.maxTokens || 1000 }}" }
]
}
}
API Call: How Workflows Are Called
From API Agent Configuration
When an API agent wraps an N8N workflow, here's the agent configuration from storage/snapshots/agents/demo_marketing_swarm_n8n.json:
"yaml": "\n{\n \"metadata\": {\n \"name\": \"marketing-swarm-n8n\",\n \"displayName\": \"Marketing Swarm N8N\",\n \"description\": \"API agent that calls n8n webhook for marketing campaign swarm processing\",\n \"version\": \"0.1.0\",\n \"type\": \"api\"\n },\n \"configuration\": {\n \"api\": {\n \"endpoint\": \"http://localhost:5678/webhook/marketing-swarm-flexible\",\n \"method\": \"POST\",\n \"headers\": {\n \"Content-Type\": \"application/json\"\n },\n \"body\": {\n \"taskId\": \"{{taskId}}\",\n \"conversationId\": \"{{conversationId}}\",\n \"userId\": \"{{userId}}\",\n \"announcement\": \"{{userMessage}}\",\n \"statusWebhook\": \"http://host.docker.internal:7100/webhooks/status\",\n \"provider\": \"{{payload.provider}}\",\n \"model\": \"{{payload.model}}\"\n },\n \"authentication\": {\n \"type\": \"none\"\n },\n \"response_mapping\": {\n \"status_field\": \"status\",\n \"result_field\": \"payload\"\n },\n \"timeout\": 120000\n },\n \"deliverable\": {\n \"format\": \"markdown\",\n \"type\": \"marketing-campaign\"\n },\n \"execution_capabilities\": {\n \"supports_converse\": false,\n \"supports_plan\": false,\n \"supports_build\": true\n }\n }\n}\n",
Key Points:
- Endpoint:
http://localhost:5678/webhook/marketing-swarm-flexible(N8N webhook URL) - Method:
POST - Body uses template variables:
{{taskId}},{{conversationId}},{{userMessage}}, etc. - CRITICAL:
statusWebhookis hardcoded here but should read from env (will be fixed)
Request Body Sent to N8N
When the API agent calls the N8N webhook, the request body looks like:
{
"taskId": "123e4567-e89b-12d3-a456-426614174000",
"conversationId": "123e4567-e89b-12d3-a456-426614174001",
"userId": "123e4567-e89b-12d3-a456-426614174002",
"announcement": "We're launching our new AI agent platform!",
"statusWebhook": "http://host.docker.internal:7100/webhooks/status",
"provider": "openai",
"model": "gpt-4"
}
Status Webhook URL Configuration
❌ WRONG - Hardcoded:
{
"statusWebhook": "http://host.docker.internal:7100/webhooks/status"
}
✅ CORRECT - From Environment:
{
"statusWebhook": "={{ process.env.API_BASE_URL || process.env.VITE_API_BASE_URL || 'http://host.docker.internal:7100' }}/webhooks/status"
}
In API Agent YAML:
"statusWebhook": "{{env.API_BASE_URL}}/webhooks/status"
Response Handling: What Helper LLM Returns
Normalized Response Format
Helper LLM returns a normalized format regardless of provider:
{
"text": "LLM response content here...",
"provider": "openai|ollama|anthropic",
"model": "gpt-4|llama2|claude-3-sonnet-20240229",
"usage": {
"prompt_tokens": 123,
"completion_tokens": 456
}
}
Key Points:
- ✅ All providers return the SAME format
- ✅
textcontains the actual response - ✅
providerandmodelidentify what was used - ✅
usagecontains token counts (if available)
Accessing Response in Workflow
After Helper LLM executes, access the response:
{
"result": "={{ $json.text }}",
"provider": "={{ $json.provider }}",
"model": "={{ $json.model }}",
"tokens": "={{ $json.usage.prompt_tokens + $json.usage.completion_tokens }}"
}
Example: Complete Workflow Response
When Marketing Swarm workflow completes, it returns:
{
"webPost": "Full blog post content...",
"seoContent": "Meta tags, keywords, JSON-LD...",
"socialMedia": "Twitter: ...\nLinkedIn: ...\nFacebook: ...",
"status": "completed",
"taskId": "123e4567-e89b-12d3-a456-426614174000",
"conversationId": "123e4567-e89b-12d3-a456-426614174001"
}
How API Agent Handles N8N Response
Response Transformation
From apps/api/src/agent-platform/services/agent-runtime-dispatch.service.ts:
const end = Date.now();
// Normalize content (apply response transform if configured)
const content = this.extractApiResponseContent(api, res.data);
const isOk = res.status >= 200 && res.status < 300;
const response = {
content,
metadata:
The API agent runner:
- Receives response from N8N webhook
- Applies
response_transformif configured (field extraction) - Formats as deliverable
- Returns to caller
Response Mapping Example
If API agent YAML has:
"response_mapping": {
"status_field": "status",
"result_field": "payload"
}
Then:
statusfield from N8N response → API response statuspayloadfield from N8N response → API response content
Complete Example: Marketing Swarm Workflow
1. Webhook Receives Request
POST http://localhost:5678/webhook/marketing-swarm-flexible
Content-Type: application/json
{
"taskId": "uuid",
"conversationId": "uuid",
"userId": "uuid",
"announcement": "We're launching our new AI agent platform!",
"statusWebhook": "${API_BASE_URL}/webhooks/status",
"provider": "openai",
"model": "gpt-4"
}
2. Workflow Extracts Parameters
Three "Set" nodes prepare parameters for three Helper LLM calls:
- Web Post (sequence: 1, temperature: 0.7)
- SEO Content (sequence: 2, temperature: 0.5)
- Social Media (sequence: 3, temperature: 0.8)
3. Each Helper LLM Call
Web Post Call:
{
"workflowId": "9jxl03jCcqg17oOy",
"fieldMapping": {
"fields": [
{ "name": "taskId", "value": "={{ $json.taskId }}" },
{ "name": "conversationId", "value": "={{ $json.conversationId }}" },
{ "name": "userId", "value": "={{ $json.userId }}" },
{ "name": "statusWebhook", "value": "={{ $json.statusWebhook }}" },
{ "name": "stepName", "value": "Write Blog Post" },
{ "name": "sequence", "value": 1 },
{ "name": "totalSteps", "value": 3 },
{ "name": "userMessage", "value": "={{ $json.announcement }}" },
{ "name": "systemMessage", "value": "You are a brilliant blog post writer..." },
{ "name": "provider", "value": "={{ $json.provider }}" },
{ "name": "model", "value": "={{ $json.model }}" },
{ "name": "temperature", "value": 0.7 },
{ "name": "maxTokens", "value": 1000 }
]
}
}
4. Helper LLM Returns Response
{
"text": "Full blog post content here...",
"provider": "openai",
"model": "gpt-4",
"usage": {
"prompt_tokens": 150,
"completion_tokens": 800
}
}
5. Workflow Combines Results
{
"webPost": "Full blog post...",
"seoContent": "SEO content...",
"socialMedia": "Social media posts...",
"status": "completed"
}
6. API Agent Returns to Caller
{
"success": true,
"mode": "build",
"payload": {
"content": "Full blog post...\n\nSEO content...\n\nSocial media posts...",
"metadata": {
"provider": "external_api",
"model": "n8n_workflow",
"status": "completed"
}
}
}
Status Webhook Format
Start Status (Sent by Helper LLM)
{
"taskId": "uuid",
"status": "running",
"timestamp": "2025-01-12T10:00:00.000Z",
"step": "Write Blog Post",
"message": "Starting Write Blog Post",
"sequence": 1,
"totalSteps": 3,
"conversationId": "uuid",
"userId": "uuid"
}
End Status (Sent by Helper LLM)
{
"taskId": "uuid",
"status": "completed",
"timestamp": "2025-01-12T10:01:00.000Z",
"step": "Write Blog Post",
"message": "Completed Write Blog Post",
"sequence": 1,
"totalSteps": 3,
"conversationId": "uuid",
"userId": "uuid"
}
Temperature Guidelines
| Use Case | Temperature | Max Tokens | Example |
|---|---|---|---|
| Factual/Analytical | 0.5 |
800 |
SEO content, data analysis |
| General Purpose | 0.7 |
1000 |
Blog posts, general content |
| Creative | 0.8 |
1200 |
Social media, marketing copy |
Common Mistakes
❌ Mistake 1: Missing Required Parameters
// ❌ WRONG - Missing status tracking parameters
{
"userMessage": "Write a blog post",
"provider": "openai"
}
Fix: Include all required parameters:
{
"userMessage": "Write a blog post",
"provider": "openai",
"taskId": "uuid",
"conversationId": "uuid",
"userId": "uuid",
"statusWebhook": "${API_BASE_URL}/webhooks/status",
"stepName": "write_blog",
"sequence": 1,
"totalSteps": 1
}
❌ Mistake 2: Hardcoded Status Webhook
// ❌ WRONG
{
"statusWebhook": "http://host.docker.internal:7100/webhooks/status"
}
Fix: Read from environment:
{
"statusWebhook": "={{ process.env.API_BASE_URL + '/webhooks/status' }}"
}
❌ Mistake 3: Wrong Sequence Numbers
// ❌ WRONG - Sequence starts at 0
{
"sequence": 0,
"totalSteps": 3
}
Fix: Sequence is 1-based:
{
"sequence": 1,
"totalSteps": 3
}
❌ Mistake 4: Not Using Helper LLM
// ❌ WRONG - Direct LLM API call
{
"url": "https://api.openai.com/v1/chat/completions",
"body": { ... }
}
Fix: Use Helper LLM workflow (9jxl03jCcqg17oOy)
Checklist for N8N Workflows
When creating workflows that use Helper LLM:
- Webhook extracts all required parameters from
$json.body - Status webhook reads from environment (not hardcoded)
- All Helper LLM calls include:
taskId,conversationId,userId,statusWebhook,stepName,sequence,totalSteps -
stepNameis descriptive and unique -
sequenceis 1-based and sequential -
totalStepsmatches actual number of steps - Helper LLM workflow ID is
9jxl03jCcqg17oOy - Response handling accesses
$json.textfor content - Temperature set appropriately (0.5 factual, 0.7 general, 0.8 creative)
- Workflow returns normalized format
Related Documentation
- Parameters Reference: PARAMETERS.md - Complete parameter documentation
- Helper LLM Pattern:
obsidian/Team Vaults/Matt/AI Coding Environment/n8n-Workflow-Patterns.md - API Agent Development: See API Agent Development Skill for wrapping workflows as agents