How to Integrate Claude Code with n8n: Event-Driven AI Automation (2026)
n8n handles events, webhooks, and workflow orchestration. Claude Code handles reasoning, file operations, and autonomous tasks. Together they cover everything a modern automation stack needs. This guide shows the exact integration patterns we use in production.
Our setup: We run 20+ Claude Code agents with n8n on Hostinger (self-hosted). n8n handles email sequences, Telegram notifications, Supabase writes, and webhook routing. Claude Code agents handle content creation, analysis, reporting, and browser automation. Total cost: ~€300/month.
Why n8n + Claude Code (not either alone)
Both tools have hard limits that the other fills:
| Capability | n8n | Claude Code |
|---|---|---|
| Event triggers (webhooks, cron, email) | ✓ native | Manual only |
| Service integrations (Gmail, Slack, Sheets) | ✓ 400+ nodes | Via bash/API only |
| Long-running autonomous tasks | Limited (timeout) | ✓ native |
| Reasoning over unstructured data | Basic AI nodes | ✓ native |
| File operations, code execution | Limited | ✓ native |
| Multi-step decision trees | Complex to build | ✓ via CLAUDE.md |
The split: n8n owns events and integrations. Claude Code owns reasoning and execution. n8n is the nervous system. Claude Code is the brain.
Pattern 1: n8n Triggers a Claude Code Agent via SSH
The simplest integration: n8n detects an event (new email, form submission, schedule) and fires a Claude Code agent on your server.
n8n workflow setup
In n8n, use an SSH node to run Claude Code on your VPS:
# n8n SSH node configuration:
Host: your-server-ip
Port: 22
Username: claude
Auth: Private Key
# Command to execute:
cd /home/claude/project && claude -p "{{ $json.task }}" \
--allowedTools Read,Write,Bash,LS \
--output-format json 2>&1
In the n8n node, map the task variable from whatever triggered the workflow (form data, email subject, webhook body).
Passing data to the agent
# Write task data to a file before running the agent
echo '{{ JSON.stringify($json) }}' > /tmp/n8n-task.json && \
cd /home/claude/project && \
claude -p "Read /tmp/n8n-task.json and process the task defined there" \
--allowedTools Read,Write,Bash,LS
Why write to file instead of passing inline? JSON in bash strings breaks on special characters. Writing to a file first is reliable at any data size. The agent reads it with the Read tool automatically.
Reading the result back in n8n
Have the agent write output to a known file, then read it in n8n:
# In CLAUDE.md:
## Output
Write your result as JSON to /tmp/agent-output.json:
{"status": "done", "result": "...", "errors": []}
# n8n: second SSH node reads the file:
cat /tmp/agent-output.json
Pattern 2: Claude Code Agent Calls n8n Webhooks
The reverse: a Claude Code agent decides to trigger something in n8n — send an email, notify a Slack channel, write to a database.
In your CLAUDE.md
# CLAUDE.md — Agent with n8n notification
## Escalation (use when task is complete or blocked)
Call the n8n webhook to report results:
curl -s -X POST https://your-n8n-host/webhook/agent-report \
-H "Content-Type: application/json" \
-d '{"agent": "content-writer", "status": "done", "output": "report.md", "errors": 0}'
## Trigger conditions
- Call webhook AFTER writing final output to file
- Call webhook if error occurs more than twice
- NEVER call webhook without a status and output path
n8n webhook receiver workflow
In n8n: Webhook node → Switch node (route by status) → branches:
- status=done: Send success notification via Telegram/email
- status=error: Create Notion task, alert on Telegram with error details
- status=escalate: Send to human-in-the-loop flow (see Pattern 4)
# n8n webhook receives from agent, routes by status
Webhook (POST /webhook/agent-report)
→ Switch on {{ $json.body.status }}
→ "done" → Telegram: "✅ {{ $json.body.agent }} complete"
→ "error" → Telegram: "❌ {{ $json.body.agent }}: {{ $json.body.error }}"
→ "escalate"→ Gmail + Telegram + Notion task create
Pattern 3: Shared State via Supabase
For multi-agent systems where agents and n8n workflows need to share state, a shared database is cleaner than direct HTTP calls.
Agents read: pending tasks, new signups, config flags
Agents write: task results, heartbeats, KPI data
n8n reads: agent outputs to trigger emails/notifications
n8n: Write to Supabase on signup
# n8n HTTP Request node:
Method: POST
URL: http://your-supabase-host/rest/v1/trial_signups
Headers:
apikey: {{ $env.SUPABASE_ANON_KEY }}
Content-Type: application/json
Prefer: return=minimal
Body:
{
"email": "{{ $json.body.email }}",
"name": "{{ $json.body.name }}",
"signed_up_at": "{{ $now.toISO() }}",
"status": "active"
}
Claude Code agent: Read pending tasks from Supabase
# In agent bash tool:
curl -s "http://your-supabase-host/rest/v1/tasks?status=eq.pending&limit=5" \
-H "apikey: $SUPABASE_ANON_KEY" \
-H "Authorization: Bearer $SUPABASE_ANON_KEY" | jq '.[] | {id, task_type, payload}'
Self-hosted Supabase tip: If you're running Supabase on your own VPS (we use Hostinger), the URL is your server IP, not the cloud console URL. The anon key comes from your .env file, not the Supabase dashboard.
Pattern 4: Human-in-the-Loop Escalation
The most important pattern for production: when an agent hits something it can't handle, it escalates through n8n to a human — then waits for the response.
Agent side (CLAUDE.md)
## Escalation triggers — call webhook and STOP
Conditions:
- Task requires spending > €50
- Output would be sent externally (email, social post)
- Error on same step more than 2 times
- Decision requires information not in context
When escalating:
1. Write current state to /tmp/escalation-state.json
2. POST to https://n8n-host/webhook/human-escalation
3. STOP. Do not continue until webhook confirms.
Escalation payload:
{
"agent": "your-agent-name",
"reason": "brief description",
"state_file": "/tmp/escalation-state.json",
"question": "what do you need from human?"
}
n8n escalation workflow
Webhook (POST /webhook/human-escalation)
→ Telegram: "🚨 Agent needs input: {{ $json.body.question }}"
→ Wait for Webhook (POST /webhook/human-response)
→ SSH: echo '{{ $json.body.answer }}' > /tmp/human-response.txt
→ Telegram: "✅ Response sent to agent"
The agent polls for the response file:
# In agent bash — poll every 30s up to 10 min
for i in $(seq 1 20); do
if [ -f /tmp/human-response.txt ]; then
cat /tmp/human-response.txt
break
fi
sleep 30
done
Our Production Architecture
Here's exactly how we wire n8n and Claude Code in our live system:
├── Trial signup → Supabase INSERT → 6-email sequence start
├── Upgrade request → Telegram Daniel + Supabase log
├── Cron 09:00 → SSH → content-writer agent → blog post published
├── Cron hourly → SSH → kpi-agent → Supabase KPI table
└── Watchdog timer → check agent heartbeats → alert if stale
EXECUTION (Claude Code on netcup VPS)
├── CEO agent → reads strategy_status.md → coordinates HoDs
├── Marketing agent → writes posts → webhook to n8n → Telegram
├── ICT agent → monitors services → SSH to ops server if issues
└── Strategy agent → KPI checks → writes shared-memory/ → webhook alert
Key wiring rules we learned
- n8n owns time-based triggers. Don't put crons inside Claude Code sessions — n8n crons are more reliable and visible.
- Agents own multi-step reasoning. Don't try to replicate complex decision trees in n8n's visual editor — it becomes unmaintainable.
- Supabase is the single source of truth. Neither n8n nor agents should be the authoritative store — both read/write to the database.
- Telegram is the human interface. n8n sends all agent outputs, errors, and escalations to Telegram. No email to the developer — it gets lost.
Common mistake: Calling Claude via the n8n AI Agent node for long-running tasks. The n8n AI Agent node has execution time limits and doesn't support file system access. Use SSH to run Claude Code on a server for anything requiring tools or lasting more than 30 seconds.
Minimal working example: Email-to-Agent pipeline
Full end-to-end: new email arrives → Claude Code analyzes it → response drafted → Telegram approval → sent.
# n8n workflow (simplified):
Gmail Trigger (new email)
→ Filter: not promotional
→ SSH node: echo '{{ $json }}' > /tmp/email.json
→ SSH node: claude -p "Read /tmp/email.json, draft a reply in /tmp/reply.txt"
→ Read File: /tmp/reply.txt
→ Telegram: "Reply draft:\n{{ $json.data }}\n\nSend? Reply YES/NO"
→ Wait for Telegram response
→ If YES: Gmail Send node
→ If NO: Telegram "Discarded. Reply with edits to resend."
Want the full n8n + Claude Code workflow templates?
The 7-day free trial includes our production n8n workflow exports, the CLAUDE.md files for each agent tier, and the Supabase schema we use. Real files, not examples built for a course.
Start Free Trial →