{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "82242ae4-4639-484b-8e48-c5f9e7fc56f1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -400,
        528
      ],
      "parameters": {
        "width": 500,
        "height": 644,
        "content": "### GitHub issue triage with Gemini AI\nAutomatically classifies new issues, adds labels, and routes alerts by priority.\n\n### How it works\n1. A **webhook** fires when a new issue is opened on your repo\n2. **Gemini AI** reads the title and body, then classifies the type (Bug, Feature, Question, etc.) and priority (Critical \u2192 Low)\n3. Labels are added to the issue via the GitHub API, and an AI summary is posted as a comment\n4. A **Switch** routes Critical/High to #urgent-issues and Medium/Low to #issue-tracker on Slack\n5. Every triaged issue is logged to **Google Sheets** for tracking\n\n### Setup steps\n1. Set the webhook URL in your GitHub repo \u2192 Settings \u2192 Webhooks \u2192 select \"Issues\" events\n2. Add your Gemini API key as HTTP Header Auth credentials (`x-goog-api-key`)\n3. Create a GitHub personal access token with `issues:write` scope and add as HTTP Header Auth\n4. Connect Slack and Google Sheets OAuth2 credentials\n5. Create Slack channels: `#urgent-issues` and `#issue-tracker`\n\n### Customization\n- Edit the categories in the Gemini prompt to fit your project\n- Change the priority threshold in the Switch node\n- Add more Slack channels for different teams"
      },
      "typeVersion": 1
    },
    {
      "id": "44b185f3-6481-4b5e-8e4a-434cfb73dd58",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 480,
        "content": "## Capture new issues\nWebhook receives the GitHub event payload. The Set node picks out the fields we actually need for classification."
      },
      "typeVersion": 1
    },
    {
      "id": "9cedae3f-f26f-4ff6-9665-edc6f42ef497",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 480,
        "content": "## AI classification\nGemini reads the issue and returns type, priority, and a one-line summary as JSON. The next node parses the response."
      },
      "typeVersion": 1
    },
    {
      "id": "23b154fe-a09a-4999-be47-91e8f261a8a4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 480,
        "content": "## Update the issue\nPOSTs labels and an AI analysis comment back to GitHub via the REST API."
      },
      "typeVersion": 1
    },
    {
      "id": "3f1e5df5-a57e-42b6-9ecb-1c8ee53959e2",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 480,
        "content": "## Route alerts & log\nCritical/High \u2192 #urgent-issues, Medium/Low \u2192 #issue-tracker. Everything gets appended to Sheets for analytics."
      },
      "typeVersion": 1
    },
    {
      "id": "14c49277-33b1-49d0-8d7d-51fab0f4da98",
      "name": "GitHub Issue Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        176,
        704
      ],
      "parameters": {
        "path": "github-issue-webhook",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "cd56005c-66c3-4926-b4bb-f7337c548e16",
      "name": "Extract Issue Data",
      "type": "n8n-nodes-base.set",
      "position": [
        416,
        704
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f1",
              "name": "issue_title",
              "type": "string",
              "value": "={{ $json.body.issue.title }}"
            },
            {
              "id": "f2",
              "name": "issue_body",
              "type": "string",
              "value": "={{ $json.body.issue.body }}"
            },
            {
              "id": "f3",
              "name": "issue_number",
              "type": "number",
              "value": "={{ $json.body.issue.number }}"
            },
            {
              "id": "f4",
              "name": "issue_url",
              "type": "string",
              "value": "={{ $json.body.issue.html_url }}"
            },
            {
              "id": "f5",
              "name": "author",
              "type": "string",
              "value": "={{ $json.body.issue.user.login }}"
            },
            {
              "id": "f6",
              "name": "repo_name",
              "type": "string",
              "value": "={{ $json.body.repository.name }}"
            },
            {
              "id": "f7",
              "name": "repo_owner",
              "type": "string",
              "value": "={{ $json.body.repository.owner.login }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "12c0d793-057d-4a50-a206-f96ca89eb865",
      "name": "Classify with Gemini AI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        704,
        704
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"contents\": [{\n    \"parts\": [{\n      \"text\": \"Classify this GitHub issue. Return ONLY valid JSON, no markdown.\\n\\nTitle: {{ $json.issue_title }}\\n\\nBody: {{ $json.issue_body }}\\n\\nReturn JSON with:\\n- type: one of Bug, Feature Request, Documentation, Enhancement, Question\\n- priority: one of Critical, High, Medium, Low\\n- summary: one sentence analysis\"\n    }]\n  }],\n  \"generationConfig\": {\n    \"responseMimeType\": \"application/json\",\n    \"responseSchema\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": { \"type\": \"string\" },\n        \"priority\": { \"type\": \"string\" },\n        \"summary\": { \"type\": \"string\" }\n      },\n      \"required\": [\"type\", \"priority\", \"summary\"]\n    }\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "b7877b46-2d2b-4e76-ad27-294df27845dd",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.set",
      "position": [
        944,
        704
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "p1",
              "name": "ai_type",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).type }}"
            },
            {
              "id": "p2",
              "name": "ai_priority",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).priority }}"
            },
            {
              "id": "p3",
              "name": "ai_summary",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).summary }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "7eb88821-1f42-499a-a92a-4e525e509824",
      "name": "Add Labels to Issue",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1216,
        704
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{ $json.repo_owner }}/{{ $json.repo_name }}/issues/{{ $json.issue_number }}/labels",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"labels\": [\"{{ $json.ai_type.toLowerCase() }}\", \"priority:{{ $json.ai_priority.toLowerCase() }}\"] }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "ba10d179-0e6f-44d2-b4fa-d9d10e998296",
      "name": "Post AI Comment",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1456,
        704
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{ $json.repo_owner }}/{{ $json.repo_name }}/issues/{{ $json.issue_number }}/comments",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"body\": \"\ud83e\udd16 **AI Triage**\\n\\n**Type:** {{ $json.ai_type }}\\n**Priority:** {{ $json.ai_priority }}\\n**Summary:** {{ $json.ai_summary }}\\n\\n_Auto-generated. Adjust labels if needed._\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "5f35a814-66d9-4e83-927a-ba173d620177",
      "name": "Is Critical or High?",
      "type": "n8n-nodes-base.if",
      "position": [
        1744,
        704
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "priority-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.ai_priority }}",
              "rightValue": "Critical"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8fc8e6c9-ed0b-4cae-a11b-ba9336fcfa85",
      "name": "Alert Urgent Issues",
      "type": "n8n-nodes-base.slack",
      "position": [
        1984,
        608
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *URGENT: {{ $json.ai_type }}* in `{{ $json.repo_name }}`\n*Priority:* {{ $json.ai_priority }}\n*Issue:* {{ $json.issue_title }}\n*Author:* {{ $json.author }}\n*Analysis:* {{ $json.ai_summary }}\n<{{ $json.issue_url }}|View Issue>",
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "42f4e44f-5c7d-4411-a765-588bd563ceea",
      "name": "Log to Issue Tracker",
      "type": "n8n-nodes-base.slack",
      "position": [
        1984,
        816
      ],
      "parameters": {
        "text": "=\ud83d\udcdd *New Issue Triaged:* {{ $json.ai_type }} ({{ $json.ai_priority }})\n*Issue:* {{ $json.issue_title }} by {{ $json.author }}\n*Summary:* {{ $json.ai_summary }}\n<{{ $json.issue_url }}|View Issue>",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "60306f05-211d-46f8-b5b3-1a81b9f43eef",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2192,
        704
      ],
      "parameters": {
        "columns": {
          "value": {
            "URL": "={{ $json.issue_url }}",
            "Date": "={{ $now.toISO() }}",
            "Type": "={{ $json.ai_type }}",
            "Issue": "={{ $json.issue_title }}",
            "Author": "={{ $json.author }}",
            "Summary": "={{ $json.ai_summary }}",
            "Priority": "={{ $json.ai_priority }}",
            "Repository": "={{ $json.repo_name }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Repository",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Repository",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Issue",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Issue",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Author",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Author",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 4.5
    }
  ],
  "connections": {
    "Post AI Comment": {
      "main": [
        [
          {
            "node": "Is Critical or High?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Add Labels to Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Issue Data": {
      "main": [
        [
          {
            "node": "Classify with Gemini AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Labels to Issue": {
      "main": [
        [
          {
            "node": "Post AI Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub Issue Webhook": {
      "main": [
        [
          {
            "node": "Extract Issue Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Critical or High?": {
      "main": [
        [
          {
            "node": "Alert Urgent Issues",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Issue Tracker",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify with Gemini AI": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}