AutomationFlowsAI & RAG › Review Jira Ticket Quality with Openrouter Gpt and Coaching Comments

Review Jira Ticket Quality with Openrouter Gpt and Coaching Comments

BySalim BRAHMI @salimbr on n8n.io

This workflow triggers on new Jira Cloud issues (Story, Bug, or Task), uses OpenRouter (OpenAI model) to score ticket quality across five criteria, and posts an automated coaching comment back to Jira only when the ticket falls below a configurable threshold. Triggers when a new…

Event trigger★★★★☆ complexityAI-powered13 nodesJira TriggerAgentJiraOpenRouter Chat
AI & RAG Trigger: Event Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #16693 — we link there as the canonical source.

This workflow follows the Agent → OpenRouter Chat 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 →

Download .json
{
  "id": "Qqtv43Or6stmBsmt",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "JIRA Ticket Quality Checker",
  "tags": [],
  "nodes": [
    {
      "id": "3b59846b-b1cd-4816-a405-a7786a110797",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 320,
        "content": "## Parse and route score\n\nExtracts the numeric quality score from the AI output, then checks whether it passes or fails the configured threshold."
      },
      "typeVersion": 1
    },
    {
      "id": "fe7d4f6f-f24a-43cc-b796-214ec325646c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        400
      ],
      "parameters": {
        "color": 7,
        "height": 576,
        "content": "## Comment or ignore\n\nHandles the final branch: low-quality tickets receive a Jira comment, while acceptable tickets end with no further action."
      },
      "typeVersion": 1
    },
    {
      "id": "c955ddef-8743-4acd-915e-1ce2aca310f1",
      "name": "When JIRA Ticket Created",
      "type": "n8n-nodes-base.jiraTrigger",
      "position": [
        -240,
        704
      ],
      "parameters": {
        "events": [
          "jira:issue_created"
        ],
        "additionalFields": {
          "filter": "issuetype in (Story, Bug, Task)"
        }
      },
      "credentials": {
        "jiraSoftwareCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "652534d6-e1ed-48b0-9d43-175800d561d2",
      "name": "Quality Analysis Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        336,
        704
      ],
      "parameters": {
        "text": "=Evaluate this Jira ticket:\n\n<ticket>\nType: {{ $('When JIRA Ticket Created').first().json.issue.fields.issuetype.name }}\nTitle: {{ $('When JIRA Ticket Created').first().json.issue.fields.summary }}\nDescription: {{ $('When JIRA Ticket Created').first().json.issue.fields.description ?? \"No description provided.\" }}\nReporter: {{ $('When JIRA Ticket Created').first().json.issue.fields.reporter.displayName }}\n</ticket>\n\nReturn the evaluation JSON.",
        "options": {
          "systemMessage": "You are a Jira ticket quality reviewer. Your role is to evaluate whether \na ticket contains enough information for a developer to start working on it \nwithout asking clarifying questions.\n\nEvaluate the ticket on these 5 criteria:\n\n1. **Title clarity** \u2014 Is the title specific and action-oriented? (not \"Fix bug\" or \"Update page\")\n2. **Description completeness** \u2014 Does it explain the context, the problem, and the expected behavior?\n3. **Acceptance criteria** \u2014 Are there clear, testable conditions for \"done\"?\n4. **Reproduction steps** \u2014 For bugs: are steps to reproduce present? For stories: is there a user scenario?\n5. **Scope & dependencies** \u2014 Are unknowns, blockers, or related tickets mentioned?\n\nFor each criterion, assign a score from 1 to 3:\n- 1 = Missing or too vague to be useful\n- 2 = Present but incomplete\n- 3 = Clear and sufficient\n\nOutput a JSON object with this exact structure:\n{\n  \"scores\": {\n    \"title_clarity\": 1-3,\n    \"description_completeness\": 1-3,\n    \"acceptance_criteria\": 1-3,\n    \"reproduction_steps\": 1-3,\n    \"scope_dependencies\": 1-3\n  },\n  \"total\": 5-15,\n  \"normalized_score\": 1-10,\n  \"missing\": [\"list of the criteria scored 1\"],\n  \"comment\": \"2-3 sentence coaching comment addressed directly to the ticket author. Friendly tone. Name exactly what is missing and why it matters. Do not mention the score.\"\n}\n\nReturn only the JSON object. No preamble, no markdown fences."
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "e7b66d3d-fc8c-4c30-bc92-46b97191b22e",
      "name": "Add Comment to JIRA Ticket",
      "type": "n8n-nodes-base.jira",
      "position": [
        1152,
        608
      ],
      "parameters": {
        "comment": "={{ $json.comment }}\n\n**Quality score: {{ $json.normalized_score }}/10**\nMissing: {{ $json.missing.join(', ') }}\n\n_This comment was generated automatically to help keep our backlog clean._",
        "options": {},
        "issueKey": "={{ $('When JIRA Ticket Created').item.json.issue.key }}",
        "resource": "issueComment"
      },
      "credentials": {
        "jiraSoftwareCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "64a21e7e-36c9-4b70-9a44-4a5adf05395d",
      "name": "Set Score Threshold",
      "type": "n8n-nodes-base.set",
      "position": [
        16,
        704
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f1719701-bb91-4fcf-b024-18e45ef3f41c",
              "name": "SCORE_THRESHOLD",
              "type": "number",
              "value": 7
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "203d07c6-9f28-4682-a6f7-e1b036c8f58c",
      "name": "OpenAI Quality Evaluator",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        320,
        960
      ],
      "parameters": {
        "model": "openai/gpt-oss-20b:free",
        "options": {
          "maxTokens": -1,
          "temperature": 0.9
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1f7c24d1-257a-429d-a920-b119a5d37d56",
      "name": "Parse Normalized Score",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        704
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json.output ?? $input.first().json.text ?? \"\";\n\nconst cleaned = response\n  .replace(/^```json\\s*/i, \"\")\n  .replace(/^```\\s*/i, \"\")\n  .replace(/\\s*```$/i, \"\")\n  .trim();\n\nlet scores;\n\ntry {\n  scores = JSON.parse(cleaned);\n} catch (error) {\n  throw new Error(`Invalid JSON returned by Quality Checker Agent.\\n${error.message}`);\n}\n\nreturn scores;"
      },
      "typeVersion": 2
    },
    {
      "id": "6b9ec50c-d7b7-46ba-bf74-fefbdd5d83d0",
      "name": "Check Score Threshold",
      "type": "n8n-nodes-base.if",
      "position": [
        928,
        704
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d7d49325-a7a0-4030-a53f-af4f2842798f",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.normalized_score }}",
              "rightValue": "={{ $('Set Score Threshold').item.json.SCORE_THRESHOLD }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b5f709f5-4465-40c9-baf9-4251e8d6d1ae",
      "name": "No Action Needed",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1152,
        816
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "69250667-dd1f-4cff-89ca-69f0ff6d301b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        400
      ],
      "parameters": {
        "width": 480,
        "height": 752,
        "content": "## JIRA Ticket Quality Checker\n\n### How it works\n\nThis workflow listens for Jira ticket events, sets a quality score threshold, and sends the ticket details to an AI quality-checking agent. It parses the AI response into a score, compares it with the configured threshold, and either adds a Jira comment with feedback or exits without action.\n\n### Setup steps\n\n- Configure Jira credentials for the Jira Trigger and Add a comment nodes, including the target Jira site/project events to monitor.\n- Configure the OpenRouter/OpenAI chat model credentials used by the Open AI Quality Checker sub-node.\n- Set the SCORE_THRESHOLD value in the Config node to define the minimum acceptable ticket quality score.\n- Verify the AI agent prompt and Score Parser Code output format agree on how the quality score is returned and parsed.\n\n### Customization\n\nAdjust the SCORE_THRESHOLD, AI evaluation prompt, and Jira comment text to match your team\u2019s definition of ticket quality and desired feedback style."
      },
      "typeVersion": 1
    },
    {
      "id": "4061410e-7795-40a8-b3d8-9ed6ca2346d1",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Receive Jira ticket\n\nStarts the workflow when a Jira event occurs and prepares the configurable quality threshold for downstream checks."
      },
      "typeVersion": 1
    },
    {
      "id": "64de4da4-82a0-4733-985c-c3523f66c50e",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 560,
        "content": "## Evaluate ticket quality\n\nUses an AI agent and its connected OpenRouter chat model to assess the Jira ticket\u2019s quality against the workflow\u2019s criteria."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "74af3881-4b5f-432f-b30c-b4a5fa48ebe6",
  "connections": {
    "Set Score Threshold": {
      "main": [
        [
          {
            "node": "Quality Analysis Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Score Threshold": {
      "main": [
        [
          {
            "node": "Add Comment to JIRA Ticket",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Action Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Normalized Score": {
      "main": [
        [
          {
            "node": "Check Score Threshold",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Quality Analysis Agent": {
      "main": [
        [
          {
            "node": "Parse Normalized Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Quality Evaluator": {
      "ai_languageModel": [
        [
          {
            "node": "Quality Analysis Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When JIRA Ticket Created": {
      "main": [
        [
          {
            "node": "Set Score Threshold",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow triggers on new Jira Cloud issues (Story, Bug, or Task), uses OpenRouter (OpenAI model) to score ticket quality across five criteria, and posts an automated coaching comment back to Jira only when the ticket falls below a configurable threshold. Triggers when a new…

Source: https://n8n.io/workflows/16693/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow listens for Jira issue create/update events, finds unassigned issues in a target project, pulls team member data from Airtable plus each member’s open Jira workload, and uses an OpenRout

Jira, OpenRouter Chat, Jira Tool +4
AI & RAG

SecretOps is an n8n security automation workflow that monitors Git push events, detects high-risk secrets in commits, and automatically responds in real time.

OpenRouter Chat, Github Trigger, HTTP Request +4
AI & RAG

Jira Retrospective. Uses jira, agent, lmChatOpenAi, summarize. Event-driven trigger; 13 nodes.

Jira, Agent, OpenAI Chat +3
AI & RAG

Jira users who want to *automate the generation of a Lessons Learned or Retrospective* report after an Epic is Done.

Jira, Agent, OpenAI Chat +3
AI & RAG

The AI-Powered Shopify SEO Content Automation is an enterprise-grade workflow that transforms product content creation for e-commerce stores. This sophisticated multi-agent system integrates GPT-4o, C

Perplexity Tool, Memory Buffer Window, Agent +15