6.1: n8n Workflow Patterns
Overview
n8n is an open-source, low-code workflow automation platform that excels at connecting development tools in ASPICE-compliant environments. This section covers practical patterns for requirements management, CI/CD, and development workflow automation.
Note: n8n workflow JSON examples are illustrative and may require adjustment for your n8n version and node configurations. Test workflows thoroughly before production use.
n8n Architecture
The following diagram shows n8n's event-driven architecture, illustrating how webhook triggers, scheduled polls, and manual triggers initiate ASPICE-compliant automation workflows.
Pattern 1: Requirements Sync Workflow
Use Case
Synchronize requirements between DOORS Next and Jira for cross-team collaboration. When a requirement is updated in DOORS, this workflow automatically creates or updates the corresponding Jira issue and notifies the team.
Workflow Visualization
The following diagram shows the complete Jira-DOORS synchronization workflow, illustrating how requirement change events flow through validation, transformation, and update nodes to maintain bidirectional consistency between the two systems.
Implementation: Step-by-Step
The workflow consists of 6 nodes, each handling a specific task. The following sections describe each component:
Step 1: Webhook Trigger
The workflow is triggered when DOORS Next sends a webhook notification about a requirement update.
{
"name": "DOORS Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "doors-requirement-updated",
"responseMode": "onReceived",
"options": {}
}
}
Step 2: Parse the Event Data
Extract the requirement ID, text, and status from the webhook payload. Build the DOORS URL for traceability.
{
"name": "Parse DOORS Event",
"type": "n8n-nodes-base.function",
"parameters": {
"functionCode": "const reqData = items[0].json;\nconst reqId = reqData.requirement.id;\nconst reqText = reqData.requirement.text;\nconst reqStatus = reqData.requirement.status;\n\nreturn {\n json: {\n requirementId: reqId,\n text: reqText,\n status: reqStatus,\n doorsUrl: `https://doors.example.com/rm/requirements/${reqId}`\n }\n};"
}
}
Step 3: Check if Jira Issue Already Exists
Search Jira for an existing issue with a label matching the requirement ID. This determines whether we create a new issue or update an existing one.
{
"name": "Check Jira Issue Exists",
"type": "n8n-nodes-base.jira",
"parameters": {
"resource": "issue",
"operation": "search",
"jql": "project = REQ AND labels = {{ $json.requirementId }}",
"returnAll": false,
"limit": 1
}
}
Step 4: Route to Create or Update
Based on the search result, branch the workflow. If no issue exists (total == 0), create a new one. Otherwise, update the existing issue.
{
"name": "Route: Create or Update",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"number": [{ "value1": "={{ $json.total }}", "operation": "equal", "value2": 0 }]
}
}
}
Step 5a: Create New Jira Issue (if not exists)
Create a new requirement issue in Jira with the requirement text, DOORS link, and auto-sync label.
{
"name": "Create Jira Issue",
"type": "n8n-nodes-base.jira",
"parameters": {
"resource": "issue",
"operation": "create",
"project": "REQ",
"issueType": "Requirement",
"summary": "={{ $node['Parse DOORS Event'].json.requirementId }}: {{ $node['Parse DOORS Event'].json.text.substring(0, 100) }}",
"description": "{{ $node['Parse DOORS Event'].json.text }}\\n\\nDOORS Link: {{ $node['Parse DOORS Event'].json.doorsUrl }}",
"labels": ["{{ $node['Parse DOORS Event'].json.requirementId }}", "auto-synced"]
}
}
Step 5b: Update Existing Jira Issue (if exists)
Update the existing Jira issue with the latest requirement text and status from DOORS.
{
"name": "Update Jira Issue",
"type": "n8n-nodes-base.jira",
"parameters": {
"resource": "issue",
"operation": "update",
"issueKey": "={{ $node['Check Jira Issue Exists'].json.issues[0].key }}",
"updateFields": {
"description": "{{ $node['Parse DOORS Event'].json.text }}\\n\\nDOORS Link: {{ $node['Parse DOORS Event'].json.doorsUrl }}",
"status": { "name": "={{ $node['Parse DOORS Event'].json.status }}" }
}
}
}
Step 6: Notify the Team
Send a Slack notification to inform the team about the sync. Includes requirement ID and status.
{
"name": "Notify Team",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#requirements-updates",
"text": "Requirement {{ $node['Parse DOORS Event'].json.requirementId }} synced to Jira",
"attachments": [{
"color": "#0052CC",
"fields": [
{ "title": "Requirement ID", "value": "={{ $node['Parse DOORS Event'].json.requirementId }}", "short": true },
{ "title": "Status", "value": "={{ $node['Parse DOORS Event'].json.status }}", "short": true }
]
}]
}
}
Node Connections
Define how nodes connect to form the workflow flow:
{
"connections": {
"DOORS Webhook": { "main": [[{ "node": "Parse DOORS Event", "type": "main", "index": 0 }]] },
"Parse DOORS Event": { "main": [[{ "node": "Check Jira Issue Exists", "type": "main", "index": 0 }]] },
"Check Jira Issue Exists": { "main": [[{ "node": "Route: Create or Update", "type": "main", "index": 0 }]] },
"Route: Create or Update": { "main": [
[{ "node": "Create Jira Issue", "type": "main", "index": 0 }],
[{ "node": "Update Jira Issue", "type": "main", "index": 0 }]
]},
"Create Jira Issue": { "main": [[{ "node": "Notify Team", "type": "main", "index": 0 }]] },
"Update Jira Issue": { "main": [[{ "node": "Notify Team", "type": "main", "index": 0 }]] }
}
}
What This Means: This pattern demonstrates bidirectional traceability (ASPICE SUP.8). Requirements flow from DOORS to Jira automatically, maintaining consistency across tools. The webhook trigger ensures real-time sync, while the create/update routing prevents duplicates.
Pattern 2: CI/CD Test Result Notification
Use Case
Monitor Jenkins builds and send intelligent notifications based on test results and AI-powered failure analysis. Critical failures trigger immediate alerts and automatic Jira ticket creation.
Workflow Visualization
The following diagram shows the CI/CD failure analysis workflow, illustrating how Jenkins build events are captured, test results are analyzed by an AI engine for root-cause classification, and notifications are routed to the appropriate teams based on severity.
Implementation: Step-by-Step
This workflow uses AI to analyze test failures and route notifications based on severity. The following steps describe each node:
Step 1: Get Jenkins Build Information
Retrieve build metadata when a build completes.
{
"name": "Jenkins Build Complete",
"type": "n8n-nodes-base.jenkins",
"parameters": {
"resource": "build",
"operation": "get",
"jobName": "{{ $json.jobName }}",
"buildNumber": "{{ $json.buildNumber }}"
}
}
Step 2: Extract Test Results
Fetch the detailed test report from Jenkins via its REST API.
{
"name": "Extract Test Results",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{ $json.url }}testReport/api/json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "jenkinsApi",
"method": "GET"
}
}
Step 3: AI-Powered Failure Analysis
Send failed test data to an AI service that analyzes patterns, identifies flaky tests, and suggests root causes.
{
"name": "AI Failure Analysis",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "http://ai-service:5000/analyze-test-failures",
"method": "POST",
"bodyParameters": {
"parameters": [
{ "name": "failures", "value": "={{ $json.failedTests }}" },
{ "name": "build_history", "value": "={{ $node['Get Build History'].json }}" }
]
}
}
}
Step 4: Determine Severity Level
Apply severity classification based on failure count and AI analysis. Flaky tests get lower severity.
{
"name": "Determine Severity",
"type": "n8n-nodes-base.function",
"parameters": {
"functionCode": "const failureCount = items[0].json.failCount;\nconst analysis = items[0].json.aiAnalysis;\n\nlet severity = 'low'; let color = '#36a64f';\nif (analysis.isFlaky) { severity = 'low'; color = '#ffcc00'; }\nelse if (failureCount > 10) { severity = 'critical'; color = '#ff0000'; }\nelse if (failureCount > 5) { severity = 'high'; color = '#ff6600'; }\nelse if (failureCount > 0) { severity = 'medium'; color = '#ffaa00'; }\n\nreturn { json: { ...items[0].json, severity, color } };"
}
}
| Condition | Severity | Color | Action |
|---|---|---|---|
| AI detects flaky test | Low | Yellow | Log only |
| 10+ failures | Critical | Red | Slack + Jira |
| 5-10 failures | High | Orange | Slack alert |
| 1-5 failures | Medium | Amber | Channel post |
Step 5: Route by Severity
Switch node routes the workflow to appropriate notification handlers.
{
"name": "Route by Severity",
"type": "n8n-nodes-base.switch",
"parameters": {
"rules": [
{ "value": "critical", "outputIndex": 0 },
{ "value": "high", "outputIndex": 1 },
{ "value": "medium", "outputIndex": 2 },
{ "value": "low", "outputIndex": 3 }
]
}
}
Step 6a: Critical Alert (Slack + Jira)
For critical failures, send an urgent Slack notification with @channel mention.
{
"name": "Alert: Critical",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#build-failures",
"text": "@channel CRITICAL BUILD FAILURE",
"attachments": [{
"color": "={{ $json.color }}",
"title": "Build #{{ $node['Jenkins Build Complete'].json.number }} FAILED",
"title_link": "={{ $node['Jenkins Build Complete'].json.url }}",
"fields": [
{ "title": "Failed Tests", "value": "={{ $json.failCount }}", "short": true },
{ "title": "AI Analysis", "value": "={{ $json.aiAnalysis.summary }}", "short": false }
]
}]
}
}
Step 6b: Auto-Create Jira Ticket for Critical Failures
Automatically create a high-priority bug ticket for critical build failures.
{
"name": "Create Jira Ticket",
"type": "n8n-nodes-base.jira",
"parameters": {
"resource": "issue",
"operation": "create",
"project": "BUILD",
"issueType": "Bug",
"priority": "Highest",
"summary": "Critical Build Failure - Build #{{ $node['Jenkins Build Complete'].json.number }}",
"description": "Build failed with {{ $json.failCount }} test failures.\\n\\nAI Analysis:\\n{{ $json.aiAnalysis.summary }}\\n\\nBuild URL: {{ $node['Jenkins Build Complete'].json.url }}"
}
}
What This Means: This pattern combines ASPICE SUP.9 (Problem Resolution) with intelligent automation. The AI analysis distinguishes real failures from flaky tests, reducing alert fatigue. Critical failures automatically create trackable Jira tickets, ensuring issues aren't lost.
Pattern 3: Automated Traceability Update
Use Case
Automatically update traceability links when code commits reference requirements.
Python Custom Node
"""
Custom n8n node for traceability extraction from Git commits
"""
import re
from typing import List, Dict
class TraceabilityExtractor:
def __init__(self):
self.patterns = {
'implements': r'Implements?:\s*((?:SWE|SYS|STKH)-\d+(?:\s*,\s*(?:SWE|SYS|STKH)-\d+)*)',
'tests': r'Tests?:\s*((?:TC)-\d+(?:\s*,\s*TC-\d+)*)',
'closes': r'Closes?:\s*((?:JIRA)-\d+(?:\s*,\s*JIRA-\d+)*)',
'relates': r'Relates?:\s*((?:SWE|SYS|TC)-\d+(?:\s*,\s*(?:SWE|SYS|TC)-\d+)*)'
}
def extract_references(self, commit_message: str) -> Dict[str, List[str]]:
"""Extract requirement references from commit message."""
references = {}
for ref_type, pattern in self.patterns.items():
matches = re.findall(pattern, commit_message, re.IGNORECASE)
if matches:
# Split comma-separated IDs and clean whitespace
ids = []
for match in matches:
ids.extend([id.strip() for id in match.split(',')])
references[ref_type] = list(set(ids)) # Remove duplicates
return references
def create_trace_links(self, commit_hash: str,
references: Dict[str, List[str]]) -> List[Dict]:
"""Create trace link records for database."""
links = []
for link_type, req_ids in references.items():
for req_id in req_ids:
links.append({
'source_type': 'commit',
'source_id': commit_hash,
'target_type': 'requirement',
'target_id': req_id,
'link_type': link_type,
'auto_created': True
})
return links
# n8n function node implementation
def process(items):
"""n8n function to process git commits and extract traceability."""
extractor = TraceabilityExtractor()
results = []
for item in items:
commit = item['json']
commit_hash = commit.get('hash', commit.get('sha'))
commit_message = commit.get('message', '')
# Extract references
references = extractor.extract_references(commit_message)
if references:
# Create trace links
links = extractor.create_trace_links(commit_hash, references)
results.append({
'json': {
'commit_hash': commit_hash,
'commit_message': commit_message[:100],
'references': references,
'trace_links': links,
'link_count': len(links)
}
})
return results
n8n Workflow Configuration
{
"name": "Git Commit Traceability Sync",
"nodes": [
{
"name": "Git Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "git-push",
"responseMode": "onReceived"
}
},
{
"name": "Extract Commits",
"type": "n8n-nodes-base.function",
"parameters": {
"functionCode": "return items[0].json.commits.map(commit => ({ json: commit }));"
}
},
{
"name": "Parse Traceability",
"type": "n8n-nodes-base.functionItem",
"parameters": {
"functionCode": "// Use the TraceabilityExtractor code from above\nconst extractor = new TraceabilityExtractor();\nconst references = extractor.extract_references(item.message);\nconst links = extractor.create_trace_links(item.id, references);\n\nreturn {\n commit: item,\n references,\n links\n};"
}
},
{
"name": "Update Neo4j",
"type": "n8n-nodes-base.neo4j",
"parameters": {
"query": "UNWIND $links AS link\nMERGE (s:Commit {id: link.source_id})\nMERGE (t:Requirement {id: link.target_id})\nMERGE (s)-[r:IMPLEMENTS {auto_created: link.auto_created}]->(t)\nRETURN count(r) as created_links",
"parameters": {
"links": "={{ $json.links }}"
}
}
},
{
"name": "Update DOORS",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://doors.example.com/api/requirements/{{ $json.target_id }}/links",
"method": "POST",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "doorsApi",
"body": {
"link_type": "implemented_by",
"target_id": "={{ $json.commit.id }}",
"target_url": "https://github.com/org/repo/commit/{{ $json.commit.id }}"
}
}
}
]
}
Summary
n8n Workflow Patterns for ASPICE development:
- Low-Code Integration: Visual workflow designer for rapid automation
- Extensible: Custom nodes for specialized logic
- Tool Agnostic: Connects DOORS, Jira, Git, Jenkins, and more
- Scalable: Handle high-volume events efficiently
- Maintainable: Visual workflows are self-documenting
Best Practices:
- Use error handling and retry logic for external API calls
- Implement circuit breakers for unstable services
- Monitor workflow execution metrics
- Version control workflow JSON definitions
- Test workflows in staging before production deployment