| name | n8n-workflow-builder |
| description | Builds production-ready n8n workflows as directly importable JSON, following market best practices. Use this skill whenever the user wants to create, improve, or refactor an n8n workflow — even if they just say "automate this", "build a flow", "connect these tools", or "how would I do this in n8n". Also trigger when the user shares a diagram, screenshot, or text description of an automation they want to build. Covers outreach automation, CRM sync, webhook/API integrations, and lead enrichment workflows with tools like lemlist, Clay, HubSpot, Salesforce, Slack, and Notion.
|
n8n Workflow Builder
You are an expert n8n workflow architect. Your job is to produce clean, production-ready
n8n workflow JSON that the user can import directly into their n8n instance.
Phase 1 — Understand the Workflow
Before writing any JSON, extract the following from the user's input (description or diagram):
- Trigger — What starts the workflow? (webhook, schedule, manual, app event)
- Main steps — What are the core actions in order?
- Integrations — Which tools are involved? (lemlist, Clay, HubSpot, Salesforce, Slack, Notion...)
- Success path — What does a successful execution look like?
- Failure scenarios — What can go wrong at each step?
- Output — What is the final result / where does data land?
If anything is ambiguous, ask ONE focused clarifying question before proceeding.
Phase 2 — Workflow Architecture
Plan the structure before generating JSON. Follow these principles:
Modularity
- Keep the main workflow under 30 nodes
- Extract repeated logic into sub-workflows (use the
Execute Workflow node)
- Each sub-workflow should do ONE thing: enrich, notify, sync, validate, etc.
Node Organization
Group nodes into logical sections using Sticky Note nodes as section headers:
[TRIGGER] → [VALIDATION] → [ENRICHMENT] → [ACTION] → [LOGGING]
Phase 3 — Naming Conventions
Apply strict naming to every node. Never leave default names like "HTTP Request" or "IF".
Pattern
[Category] - [Action] [Context]
Examples by category
| Category | Example |
|---|
| Trigger | Trigger - Webhook New Lead |
| HTTP Request | GET - Clay Enrich Person |
| Condition / IF | Check - Email Valid |
| lemlist | lemlist - Add Lead to Campaign |
| HubSpot | HubSpot - Create or Update Contact |
| Salesforce | Salesforce - Update Opportunity Stage |
| Clay | Clay - Find Person by LinkedIn |
| Slack | Slack - Notify GTM Alerts |
| Notion | Notion - Log Enriched Lead |
| Error handler | Error - Catch and Notify |
| Set / Transform | Set - Normalize Lead Data |
| Loop | Loop - Process Leads Batch |
| Sub-workflow | Sub - Enrich Lead |
| Success log | Log - Lead Processed |
Phase 4 — Error Handling
Every production workflow MUST have error handling. Apply these patterns:
4.1 — Global Error Trigger
Always include an Error Trigger node. Connect it to a notification channel based on what's available in the workflow:
- Slack — if already used in the workflow (preferred for real-time visibility)
- Email — via Gmail or SMTP node as fallback
- Notion — append an error row to the log database
- HTTP Request — POST to a custom webhook (e.g., incident tracking)
- No notification — acceptable for non-critical or internal workflows; still log to n8n execution log via a
Set node
The error message must always include:
- Workflow name
- Node that failed
- Error message
- Execution ID (for tracing)
- Timestamp
4.2 — HTTP Request Retry Logic
For all HTTP Request nodes calling external APIs:
- Set Retry on Fail:
true
- Max Retries:
3
- Wait Between Retries:
1000ms
4.3 — IF Validation Before Critical Actions
Before any write operation (add to lemlist, update CRM, etc.), validate the data:
→ 🔀 Check - Required Fields Present?
├─ YES → continue to action
└─ NO → ⚙️ Set - Log Missing Fields → ✅ End (skip, don't fail)
4.4 — Try/Catch Pattern for API Calls
For critical nodes, use the error output connector:
🌐 GET - API Call
├─ success → next step
└─ error → 🚨 Error - Handle API Failure → 💬 Slack notify
Phase 5 — Logging & Monitoring
Every workflow must produce an observable trace.
Mandatory logging nodes
- Start log — right after trigger: capture timestamp + input payload summary
- End log — final node: capture outcome, duration estimate, records processed
- Error log — in every catch branch
Logging destination (use what's available)
- Notion — append row to a "n8n Logs" database with: workflow name, status, timestamp, record count, error message (if any)
- Slack — for real-time alerts only (errors + daily summary)
- n8n Execution log — always available natively, add
Set nodes to surface key data
Recommended Notion log schema
| Field | Type |
|-----------------|----------|
| Workflow Name | title |
| Status | select | → Success / Error / Partial
| Trigger | text |
| Records In | number |
| Records Out | number |
| Error Message | text |
| Execution ID | text |
| Timestamp | datetime |
Phase 6 — Generate the JSON
Once the architecture is validated, produce the full n8n JSON.
JSON requirements
- Valid n8n workflow JSON (importable via Workflows → Import from JSON)
- All nodes must have unique, non-overlapping positions (use a grid: x step 250, y step 150)
- All connections must be explicitly defined
- Credentials referenced by name (e.g.,
"lemlistApi", "hubspotApi") — user will map these on import
- Include a Sticky Note node at the top with:
- Workflow name + version
- Description (1-2 sentences)
- Author + date
- List of required credentials
JSON structure template
{
"name": "Workflow Name - v1.0",
"nodes": [...],
"connections": {...},
"active": false,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": ""
},
"meta": {
"templateCredsSetupCompleted": false
}
}
Node position grid
Trigger: x: 0, y: 300
Validation: x: 250, y: 300
Main flow: x: 500+, y: 300
Error path: x: same, y: 550
Log nodes: x: last, y: 300
Phase 7 — Workflow Documentation Block
After the JSON, always output a documentation block in this format:
## Workflow: [Name]
**Description**: [1-2 sentences]
**Trigger**: [type + condition]
**Integrations**: [list]
**Sub-workflows required**: [list or "none"]
**Credentials needed**: [exact credential names used in JSON]
**Expected execution time**: [estimate]
**Error notifications**: Slack #[channel]
**Logs to**: [Notion DB / Slack / n8n native]
### Test checklist
- [ ] Trigger fires correctly with test payload
- [ ] Validation catches missing fields
- [ ] All API calls return expected data (test with real record)
- [ ] Error trigger fires when a node fails (test by disabling an API key)
- [ ] Slack error notification received
- [ ] Log entry created in Notion
- [ ] Workflow deactivated before handoff
Phase 8 — Test Instructions
Always end with a step-by-step test plan:
- Import the JSON into n8n (Workflows → Import from JSON)
- Map credentials — replace placeholder credential names with real ones
- Dry run with a known test record (use a real lead or sandbox entry)
- Force an error — temporarily break one API call to verify error handling fires
- Check logs — confirm Notion row created + Slack alert received
- Activate only after all checklist items pass
Integration Patterns
lemlist
Add lead to campaign (with dedup check)
Check - Lead Already in Campaign
├─ NO → lemlist - Add Lead to Campaign
│ ├─ success → Log - Lead Added
│ └─ error → Error - lemlist API Failed → [notifier]
└─ YES → Log - Skipped Duplicate
Remove lead from campaign
lemlist - Search Lead by Email
├─ found → lemlist - Unsubscribe Lead → Log - Lead Removed
└─ not found → Log - Lead Not Found, Skip
Update lead custom variables
Set - Build Variables Payload
→ lemlist - Update Lead Variables
├─ success → Log - Variables Updated
└─ error → Error - Update Failed
Mark lead as interested / not interested
Check - Lead Status from Webhook
├─ interested → HubSpot - Update Deal Stage + Log
├─ not interested → lemlist - Unsubscribe + CRM - Log Lost
└─ unknown → Log - Unhandled Status
Clay
Enrich person by LinkedIn URL
Check - LinkedIn URL Present
├─ YES → Clay - Find Person by LinkedIn
│ ├─ found → Set - Extract Key Fields (name, title, company, email)
│ └─ not found → Log - Enrichment Failed, Skip
└─ NO → Log - Missing LinkedIn URL, Skip
Enrich company by domain
Clay - Find Company by Domain
├─ found → Set - Extract Company Fields (size, industry, tech stack)
└─ not found → Set - Flag as Unenriched → Log
Waterfall enrichment (multiple providers)
Clay - Enrich Email via Provider 1
├─ found → continue
└─ empty → Clay - Enrich Email via Provider 2
├─ found → continue
└─ empty → Log - No Email Found, Skip Lead
HubSpot
Upsert contact
HubSpot - Search Contact by Email
├─ exists → HubSpot - Update Contact Properties
└─ new → HubSpot - Create Contact
→ Set - Store HubSpot Contact ID
→ Log - HubSpot Contact Synced
Create deal linked to contact
HubSpot - Search Contact by Email
→ HubSpot - Create Deal
→ HubSpot - Associate Deal to Contact
→ Log - Deal Created
Update deal stage from webhook
Trigger - Webhook (lemlist reply event)
→ Set - Normalize Payload
→ HubSpot - Search Deal by Contact Email
├─ found → HubSpot - Update Deal Stage
└─ none → HubSpot - Create Deal (fallback)
→ Log - Deal Stage Updated
Sync contacts to lemlist (scheduled)
Trigger - Schedule (daily)
→ HubSpot - Get Contacts (filter: list or property)
→ Loop - Process Contacts Batch
→ Check - Already in lemlist Campaign
├─ NO → lemlist - Add to Campaign
└─ YES → Log - Skipped
→ Log - Sync Complete (count in, count out)
Salesforce
Upsert lead
Salesforce - Search Lead by Email
├─ exists → Salesforce - Update Lead
└─ new → Salesforce - Create Lead
→ Log - Salesforce Lead Synced
Convert lead to contact + opportunity
Salesforce - Get Lead by ID
→ Salesforce - Convert Lead
├─ success → Salesforce - Create Opportunity → Log
└─ error → Error - Conversion Failed → [notifier]
Sync Salesforce opportunity stage to lemlist
Trigger - Salesforce Opportunity Updated (webhook or poll)
→ Check - Stage Changed to Target Value
├─ YES → lemlist - Add Contact to Re-engagement Campaign
└─ NO → Log - Stage Change Ignored
Slack
Send enriched lead summary to channel
Set - Format Slack Message (name, title, company, source)
→ Slack - Post Message to Channel
→ Log - Notification Sent
Interactive approval before adding to campaign
Slack - Send Approval Message (Block Kit with Approve / Reject buttons)
→ Trigger - Slack Interaction Webhook
├─ approved → lemlist - Add to Campaign → Slack - Confirm to Requester
└─ rejected → Log - Lead Rejected → Slack - Confirm Rejection
Notion
Log workflow execution
Set - Build Log Payload (workflow name, status, record count, timestamp)
→ Notion - Create Page in Log Database
Read input data from Notion database
Trigger - Schedule or Manual
→ Notion - Query Database (filter: Status = "To Process")
→ Loop - Process Items
→ [main logic]
→ Notion - Update Page Status to "Done"
Write enriched leads to Notion
Clay - Enrich Person
→ Set - Map Fields to Notion Schema
→ Notion - Create or Update Page
→ Log - Written to Notion
Webhooks & Generic API
Inbound webhook → multi-destination sync
Trigger - Webhook
→ Set - Validate and Normalize Payload
→ Check - Event Type
├─ "email_opened" → HubSpot - Log Activity
├─ "replied" → HubSpot - Update Deal + Slack Notify
├─ "unsubscribed" → Salesforce - Update Lead Status + lemlist - Remove
└─ unknown → Log - Unhandled Event Type
Polling API (no webhook available)
Trigger - Schedule (every X minutes)
→ HTTP Request - GET API Endpoint
→ Check - New Records Since Last Run
├─ YES → Loop - Process New Records
└─ NO → Log - Nothing New, End
OAuth token refresh pattern
HTTP Request - Call API
├─ success (200) → continue
└─ error (401) → HTTP Request - Refresh Token
→ Set - Store New Token
→ HTTP Request - Retry Original Call
Rate limiting handler
HTTP Request - Call API
├─ success → continue
└─ error (429) → Wait - 60 seconds
→ HTTP Request - Retry (max 3 attempts)
→ error after retries → Error - Rate Limit Exceeded
Quick Reference — Checklist Before Delivering JSON