This workflow follows the Executecommand → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "AIDP - Main Workflow v2",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "aidp-trigger",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-trigger",
"name": "Jira Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "check-label",
"leftValue": "={{ $json.issue.fields.labels.map(l => l.name || l).join(',') }}",
"rightValue": "ai-task",
"operator": {
"type": "string",
"operation": "contains"
}
},
{
"id": "check-status",
"leftValue": "={{ $json.issue.fields.status.name }}",
"rightValue": "In Progress",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "filter-conditions",
"name": "Filter AI Tasks",
"type": "n8n-nodes-base.filter",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Extract and validate required fields from Jira issue\nconst issue = $input.item.json.issue;\nconst fields = issue.fields;\n\n// Required fields\nconst jiraKey = issue.key;\nconst projectKey = jiraKey.split('-')[0];\nconst summary = fields.summary;\n\n// Handle Atlassian Document Format (ADF) description\nlet descriptionText = '';\nif (typeof fields.description === 'string') {\n descriptionText = fields.description;\n} else if (fields.description?.content) {\n const extractText = (node) => {\n if (!node) return '';\n if (node.type === 'text') return node.text || '';\n if (node.content) return node.content.map(extractText).join('');\n return '';\n };\n descriptionText = fields.description.content.map(extractText).join('\\n');\n}\n\n// Knowledge base: Project -> Default Repo mapping\nconst REPO_MAPPING = {\n 'SCRUM': 'https://github.com/andresKillem/aidp-test-repo',\n 'DEFAULT': 'https://github.com/andresKillem'\n};\n\n// Smart repo URL resolution\nlet repoUrl = fields.customfield_10039;\n\nif (!repoUrl) {\n const githubMatch = descriptionText.match(/https:\\/\\/github\\.com\\/[\\w-]+\\/[\\w.-]+/);\n if (githubMatch) {\n repoUrl = githubMatch[0].replace(/\\.git$/, '');\n }\n}\n\nif (!repoUrl) {\n repoUrl = REPO_MAPPING[projectKey] || REPO_MAPPING['DEFAULT'];\n}\n\nconst baseBranch = fields.customfield_10040 || 'main';\n\n// Parse acceptance criteria\nlet acceptanceCriteria = '';\nconst acMatch = descriptionText.match(/(?:acceptance criteria|ac)[:\\s]*([\\s\\S]*?)(?=##|$)/i);\nif (acMatch) {\n acceptanceCriteria = acMatch[1].trim();\n}\n\n// Generate branch name\nconst slugify = (text) => text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .substring(0, 50);\n\nconst branchName = `ai/${jiraKey.toLowerCase()}-${slugify(summary)}`;\n\n// Extract owner/repo from URL\nconst repoMatch = repoUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)/);\nconst owner = repoMatch ? repoMatch[1] : 'andresKillem';\nconst repo = repoMatch ? repoMatch[2].replace('.git', '') : 'aidp-test-repo';\n\nreturn {\n jiraKey,\n projectKey,\n jiraUrl: `https://polarpipeline.atlassian.net/browse/${jiraKey}`,\n summary,\n description: descriptionText,\n acceptanceCriteria,\n repoUrl,\n owner,\n repo,\n baseBranch,\n branchName,\n priority: fields.priority?.name || 'Medium',\n reporter: fields.reporter?.displayName || 'Unknown',\n timestamp: new Date().toISOString()\n};"
},
"id": "parse-issue",
"name": "Parse Issue Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
]
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const data = $input.item.json;\n\nconst promptTemplate = `# Task: ${data.summary}\n\n## Context\n\n- **Repository**: ${data.repoUrl}\n- **Base Branch**: ${data.baseBranch}\n- **Jira Issue**: [${data.jiraKey}](${data.jiraUrl})\n- **Feature Branch**: ${data.branchName}\n\n## Requirements\n\n${data.description}\n\n## Acceptance Criteria\n\n${data.acceptanceCriteria || 'Not specified - use best judgment based on requirements.'}\n\n## Instructions\n\n1. **Explore First**: Read the codebase to understand existing patterns.\n2. **Plan Before Coding**: Think through the implementation approach.\n3. **Implement Incrementally**: Make changes in logical steps with tests.\n4. **Commit with Context**: Use conventional commit format.\n5. **Self-Review**: Verify all acceptance criteria are addressed.\n\n## Constraints\n\n- DO NOT modify files unrelated to this task\n- DO NOT create a Pull Request - only commit and push\n- DO keep changes minimal and focused\n- DO follow existing patterns exactly`;\n\nreturn {\n ...data,\n prompt: promptTemplate\n};"
},
"id": "build-prompt",
"name": "Build Prompt",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
300
]
},
{
"parameters": {
"command": "={{ 'docker exec aidp-claude-executor /usr/local/bin/aidp-executor \"' + $json.repoUrl + '\" \"' + $json.branchName + '\" /dev/stdin \"' + $json.jiraKey + '\" \"' + $json.baseBranch + '\" <<EOF\\n' + $json.prompt + '\\nEOF' }}"
},
"id": "execute-claude",
"name": "Execute Claude Code",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1120,
300
]
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $input.item.json;\nconst stdout = input.stdout || '';\nlet result;\n\ntry {\n const jsonMatch = stdout.match(/\\{[\\s\\S]*\\}(?=[^}]*$)/);\n if (jsonMatch) {\n result = JSON.parse(jsonMatch[0]);\n } else {\n throw new Error('No JSON found in output');\n }\n} catch (e) {\n result = {\n status: 'error',\n error_message: `Failed to parse executor output: ${e.message}`,\n raw_output: stdout.substring(0, 2000)\n };\n}\n\n// Preserve original data\nconst originalData = $('Build Prompt').item.json;\n\nreturn {\n ...originalData,\n executorResult: result,\n success: result.status === 'success'\n};"
},
"id": "parse-result",
"name": "Parse Result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1340,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "check-success",
"leftValue": "={{ $json.success }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-success",
"name": "Success?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1560,
300
]
},
{
"parameters": {
"method": "POST",
"url": "=https://api.github.com/repos/{{ $json.owner }}/{{ $json.repo }}/pulls",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n title: '[' + $json.jiraKey + '] ' + $json.summary,\n head: $json.branchName,\n base: $json.baseBranch,\n body: '## Summary\\n\\n' + $json.summary + '\\n\\n## Jira Issue\\n\\n[' + $json.jiraKey + '](' + $json.jiraUrl + ')\\n\\n## Changes Made\\n\\n' + ($json.executorResult.claude_summary || 'See commits for details') + '\\n\\n---\\n\ud83e\udd16 *Generated by AIDP*'\n}) }}",
"options": {}
},
"id": "create-pr",
"name": "Create PR",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1780,
200
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const prResponse = $input.item.json;\nconst originalData = $('Parse Result').item.json;\n\nreturn {\n ...originalData,\n prNumber: prResponse.number,\n prUrl: prResponse.html_url\n};"
},
"id": "extract-pr-info",
"name": "Extract PR Info",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
200
]
},
{
"parameters": {
"method": "POST",
"url": "=https://api.github.com/repos/{{ $json.owner }}/{{ $json.repo }}/issues/{{ $json.prNumber }}/labels",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"labels\": [\"ai-generated\", \"needs-review\"]}",
"options": {}
},
"id": "add-labels",
"name": "Add PR Labels",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2220,
200
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=https://polarpipeline.atlassian.net/rest/api/3/issue/{{ $json.jiraKey }}/comment",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n body: {\n type: 'doc',\n version: 1,\n content: [\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: '\u2705 AIDP Completed Successfully', marks: [{ type: 'strong' }] }\n ]\n },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: '\ud83d\udd17 Pull Request: ' },\n { type: 'text', text: 'PR #' + $json.prNumber, marks: [{ type: 'link', attrs: { href: $json.prUrl } }] }\n ]\n },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Branch: ' },\n { type: 'text', text: $json.branchName, marks: [{ type: 'code' }] }\n ]\n },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Awaiting human review before merge.', marks: [{ type: 'em' }] }\n ]\n }\n ]\n }\n}) }}",
"options": {}
},
"id": "jira-comment-success",
"name": "Jira Comment Success",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2440,
200
],
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=https://polarpipeline.atlassian.net/rest/api/3/issue/{{ $json.jiraKey }}/comment",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n body: {\n type: 'doc',\n version: 1,\n content: [\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: '\u274c AIDP Failed', marks: [{ type: 'strong' }] }\n ]\n },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Error: ' + ($json.executorResult?.error_message || 'Unknown error') }\n ]\n },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Please review the task requirements and try again.', marks: [{ type: 'em' }] }\n ]\n }\n ]\n }\n}) }}",
"options": {}
},
"id": "jira-comment-error",
"name": "Jira Comment Error",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1780,
450
],
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'processed', jiraKey: $json.jiraKey }) }}"
},
"id": "respond-webhook",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2660,
300
]
}
],
"connections": {
"Jira Webhook": {
"main": [
[
{
"node": "Filter AI Tasks",
"type": "main",
"index": 0
}
]
]
},
"Filter AI Tasks": {
"main": [
[
{
"node": "Parse Issue Data",
"type": "main",
"index": 0
}
]
]
},
"Parse Issue Data": {
"main": [
[
{
"node": "Build Prompt",
"type": "main",
"index": 0
}
]
]
},
"Build Prompt": {
"main": [
[
{
"node": "Execute Claude Code",
"type": "main",
"index": 0
}
]
]
},
"Execute Claude Code": {
"main": [
[
{
"node": "Parse Result",
"type": "main",
"index": 0
}
]
]
},
"Parse Result": {
"main": [
[
{
"node": "Success?",
"type": "main",
"index": 0
}
]
]
},
"Success?": {
"main": [
[
{
"node": "Create PR",
"type": "main",
"index": 0
}
],
[
{
"node": "Jira Comment Error",
"type": "main",
"index": 0
}
]
]
},
"Create PR": {
"main": [
[
{
"node": "Extract PR Info",
"type": "main",
"index": 0
}
]
]
},
"Extract PR Info": {
"main": [
[
{
"node": "Add PR Labels",
"type": "main",
"index": 0
}
]
]
},
"Add PR Labels": {
"main": [
[
{
"node": "Jira Comment Success",
"type": "main",
"index": 0
}
]
]
},
"Jira Comment Success": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Jira Comment Error": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"staticData": null,
"tags": [
{
"name": "AIDP",
"id": "1"
}
]
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpBasicAuthhttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AIDP - Main Workflow v2. Uses executeCommand, httpRequest. Webhook trigger; 13 nodes.
Source: https://github.com/andresKillem/aidp/blob/8839fb212f6336a3aa9793485da729542e0fd7da/n8n-workflows/main-workflow-v2.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Sign PDF documents with legally-compliant digital signatures using X.509 certificates. Supports multiple PAdES signature levels (B, T, LT, LTA) with optional visible stamps.
MLOps Pipeline - Hand Talk. Uses executeCommand, httpRequest. Webhook trigger; 15 nodes.
JFCandia_Flujo. Uses executeCommand, emailSend, readWriteFile, httpRequest. Webhook trigger; 8 nodes.
SmarterOS - Update Button. Uses executeCommand, httpRequest. Webhook trigger; 5 nodes.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di