<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abdul Qadir</title>
    <description>The latest articles on DEV Community by Abdul Qadir (@abdul_qadir).</description>
    <link>https://dev.to/abdul_qadir</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831733%2F2346d211-4e93-4044-b14e-0c86b13cb207.jpg</url>
      <title>DEV Community: Abdul Qadir</title>
      <link>https://dev.to/abdul_qadir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abdul_qadir"/>
    <language>en</language>
    <item>
      <title>AI-Powered Test Generation: Jira to Xray with CrewAI: Part 2 — Agents, Tasks, and Xray Integration</title>
      <dc:creator>Abdul Qadir</dc:creator>
      <pubDate>Wed, 18 Mar 2026 01:42:32 +0000</pubDate>
      <link>https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-2-agents-tasks-and-xray-integration-3jid</link>
      <guid>https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-2-agents-tasks-and-xray-integration-3jid</guid>
      <description>&lt;p&gt;Discover how CrewAI agents can automatically convert Jira requirements into Xray test cases. Includes full setup instructions, project layout, and a ready-to-run GitHub example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vpuhm8zahaumba7nqhv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vpuhm8zahaumba7nqhv.jpeg" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 1 — Defining the CrewAI Agents
&lt;/h3&gt;

&lt;p&gt;With the project structure in place, the next step is understanding the agents that drive the workflow.&lt;/p&gt;

&lt;p&gt;In CrewAI, agents are assigned clear responsibilities. Rather than relying on a single general-purpose agent, this project uses two specialized agents: one to generate test cases and another to review and improve them.&lt;/p&gt;

&lt;p&gt;The actual agent definitions live in config/agents.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test_case_writer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QA Test Case Writer&lt;/span&gt;
  &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Write comprehensive functional test cases from Jira stories&lt;/span&gt;
  &lt;span class="na"&gt;backstory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;You are a senior QA engineer.&lt;/span&gt;
    &lt;span class="s"&gt;You write clear, structured test cases&lt;/span&gt;
    &lt;span class="s"&gt;including positive, negative, and edge scenarios.&lt;/span&gt;
  &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5-nano&lt;/span&gt;

&lt;span class="na"&gt;test_case_reviewer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QA Test Case Reviewer&lt;/span&gt;
  &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Review and improve test cases for quality and coverage&lt;/span&gt;
  &lt;span class="na"&gt;backstory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;You are a QA lead.&lt;/span&gt;
    &lt;span class="s"&gt;You ensure test cases fully cover acceptance criteria,&lt;/span&gt;
    &lt;span class="s"&gt;are clear, consistent, and complete.&lt;/span&gt;
  &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What these agents do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;QA Test Case Writer&lt;/strong&gt; reads the Jira story and creates an initial set of functional test cases.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;QA Test Case Reviewer&lt;/strong&gt; checks those test cases for clarity, completeness, and overall coverage before they are sent to Xray.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This two-agent approach mirrors a real QA workflow. One agent focuses on drafting the tests, while the second acts as a quality gate to refine the output. In many cases, it also makes sense to use different LLMs for these roles, such as one model for fast generation and another for stronger review quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this configuration matters
&lt;/h3&gt;

&lt;p&gt;Each agent definition includes a few important pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;role&lt;/strong&gt; defines the responsibility of the agent in the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;goal&lt;/strong&gt; tells the agent what outcome it should aim for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;backstory&lt;/strong&gt; gives behavioral context that helps shape the style and quality of its output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;llm&lt;/strong&gt; specifies which model powers the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;verbose: true&lt;/strong&gt; makes execution easier to inspect during development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of CrewAI’s strengths is that this behavior can be adjusted in YAML without changing core application logic. That makes the system easier to tune as your prompts, test quality standards, or workflow evolve.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll look at how these agents are assigned work through tasks.yaml, and how the overall process moves from Jira requirements to Xray-ready test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 2 — Defining the Tasks
&lt;/h3&gt;

&lt;p&gt;Once the agents are defined, the next step is assigning work to them. In CrewAI, this is done through config/tasks.yaml.&lt;/p&gt;

&lt;p&gt;This file describes what each agent should do, what input it receives, what output it should produce, and where that output should be saved. In this project, the workflow is split into two tasks: one for writing test cases and one for reviewing and publishing them.&lt;/p&gt;

&lt;p&gt;The actual task definitions look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;write_test_cases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;INPUT:&lt;/span&gt;
    &lt;span class="s"&gt;- story: Jira user story text&lt;/span&gt;

    &lt;span class="s"&gt;You are provided with a Jira user story.&lt;/span&gt;

    &lt;span class="s"&gt;Your task is to write comprehensive functional test cases based on the story.&lt;/span&gt;

    &lt;span class="s"&gt;Requirements:&lt;/span&gt;
    &lt;span class="s"&gt;- Cover positive, negative, and edge cases&lt;/span&gt;
    &lt;span class="s"&gt;- Align all test cases with the acceptance criteria&lt;/span&gt;
    &lt;span class="s"&gt;- Use clear, unambiguous language&lt;/span&gt;

    &lt;span class="s"&gt;Each test case must include:&lt;/span&gt;
    &lt;span class="s"&gt;- Test Case ID&lt;/span&gt;
    &lt;span class="s"&gt;- Title&lt;/span&gt;
    &lt;span class="s"&gt;- Preconditions&lt;/span&gt;
    &lt;span class="s"&gt;- Test Steps&lt;/span&gt;
    &lt;span class="s"&gt;- Expected Result&lt;/span&gt;

    &lt;span class="s"&gt;Jira User Story:&lt;/span&gt;
    &lt;span class="s"&gt;{story}&lt;/span&gt;

    &lt;span class="s"&gt;Output the test cases in Markdown format.&lt;/span&gt;
    &lt;span class="s"&gt;Save the output to output/test_cases_v1.md.&lt;/span&gt;
  &lt;span class="na"&gt;expected_output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;A Markdown file containing clear, well-structured functional test cases&lt;/span&gt;
    &lt;span class="s"&gt;derived from the Jira user story.&lt;/span&gt;
  &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_case_writer&lt;/span&gt;
  &lt;span class="na"&gt;output_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;output/test_cases_v1.md&lt;/span&gt;

&lt;span class="na"&gt;review_test_cases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;INPUTS:&lt;/span&gt;
    &lt;span class="s"&gt;- story: Jira user story text&lt;/span&gt;

    &lt;span class="s"&gt;You are provided with:&lt;/span&gt;
    &lt;span class="s"&gt;- The original Jira user story&lt;/span&gt;
    &lt;span class="s"&gt;- A set of test cases written in output/test_cases_v1.md&lt;/span&gt;

    &lt;span class="s"&gt;Jira User Story:&lt;/span&gt;
    &lt;span class="s"&gt;{story}&lt;/span&gt;

    &lt;span class="s"&gt;Review the existing test cases and improve them by:&lt;/span&gt;
    &lt;span class="s"&gt;- Ensuring full coverage of acceptance criteria&lt;/span&gt;
    &lt;span class="s"&gt;- Identifying and adding missing scenarios&lt;/span&gt;
    &lt;span class="s"&gt;- Improving clarity and consistency&lt;/span&gt;
    &lt;span class="s"&gt;- Removing redundancy or ambiguity&lt;/span&gt;

    &lt;span class="s"&gt;Do NOT remove valid test cases unless necessary.&lt;/span&gt;
    &lt;span class="s"&gt;You may refactor, merge, or expand them where appropriate.&lt;/span&gt;

    &lt;span class="s"&gt;Output the improved test cases in Markdown format.&lt;/span&gt;
    &lt;span class="s"&gt;Save the output to output/test_cases_v2_reviewed.md.&lt;/span&gt;

    &lt;span class="s"&gt;After generating the final improved test cases,&lt;/span&gt;
    &lt;span class="s"&gt;use the "XrayCreateTestTool" to create a Manual Test&lt;/span&gt;
    &lt;span class="s"&gt;in Xray for each finalized test case.&lt;/span&gt;

    &lt;span class="s"&gt;Pass:&lt;/span&gt;
    &lt;span class="s"&gt;- summary = Test Case Title&lt;/span&gt;
    &lt;span class="s"&gt;- description = Full test case content&lt;/span&gt;
    &lt;span class="s"&gt;- project_key = "SCRUM"&lt;/span&gt;
  &lt;span class="na"&gt;expected_output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;A reviewed and improved Markdown file containing high-quality,&lt;/span&gt;
    &lt;span class="s"&gt;acceptance-criteria-aligned test cases.&lt;/span&gt;
  &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_case_reviewer&lt;/span&gt;
  &lt;span class="na"&gt;output_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;output/test_cases_v2_reviewed.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What these tasks do
&lt;/h3&gt;

&lt;p&gt;The first task, write_test_cases, is assigned to the test_case_writer agent. Its job is to read the Jira story and generate the first version of the test cases. The output is saved to output/test_cases_v1.md.&lt;/p&gt;

&lt;p&gt;The second task, review_test_cases, is assigned to the test_case_reviewer agent. This task takes the original Jira story along with the first draft of the test cases, improves the content, and saves the final reviewed version to output/test_cases_v2_reviewed.md.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this task design works well
&lt;/h3&gt;

&lt;p&gt;This setup creates a simple but effective two-stage workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first task focuses on generation&lt;/li&gt;
&lt;li&gt;the second task focuses on refinement and delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That separation improves the final quality of the tests while keeping each agent’s responsibility clear.&lt;/p&gt;

&lt;p&gt;Another important detail is that the review task does more than just edit Markdown output. It also instructs the reviewer agent to call the XrayCreateTestTool, which creates a Manual Test issue in Xray for each finalized test case. This is the step that moves the workflow from AI-generated content to actual test management inside Jira.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key takeaway
&lt;/h3&gt;

&lt;p&gt;The tasks.yaml file acts as the operational blueprint of the system. It tells CrewAI what should happen, in what sequence, and what each stage should produce.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll look at how these agents and tasks are wired together in crew.py to create the full CrewAI workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 3 — Wiring the Workflow in crew.py
&lt;/h3&gt;

&lt;p&gt;After defining the agents and tasks, the next step is connecting them into a working workflow. In this project, that orchestration happens in crew.py.&lt;/p&gt;

&lt;p&gt;This file is where the CrewAI application is assembled. It maps the YAML configuration into Python objects, attaches tools to the right agent, and defines how the tasks should run.&lt;/p&gt;

&lt;p&gt;The core implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai.project&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CrewBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;testcrew_ai.tools.xray_tool&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XrayCreateTestTool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;

&lt;span class="nd"&gt;@CrewBase&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCrewAI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Crew class for manual test case writing and reviewing.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;agents_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config/agents.yaml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;tasks_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config/tasks.yaml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="nd"&gt;@agent&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_case_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Agent responsible for writing test cases.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_case_writer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@agent&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_case_reviewer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Agent responsible for reviewing test cases, with Xray tool.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_case_reviewer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;XrayCreateTestTool&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@task&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_test_cases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Task for writing test cases.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;write_test_cases&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@task&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;review_test_cases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Task for reviewing test cases.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;review_test_cases&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@crew&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates the manual testing crew.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What crew.py does
&lt;/h3&gt;

&lt;p&gt;This file serves as the bridge between configuration and execution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It loads agent definitions from config/agents.yaml&lt;/li&gt;
&lt;li&gt;It loads task definitions from config/tasks.yaml&lt;/li&gt;
&lt;li&gt;It creates Python Agent and Task objects using CrewAI decorators&lt;/li&gt;
&lt;li&gt;It attaches the XrayCreateTestTool to the reviewer agent&lt;/li&gt;
&lt;li&gt;It runs the workflow using a sequential process&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why the reviewer gets the Xray tool
&lt;/h3&gt;

&lt;p&gt;One important design choice in this file is that the XrayCreateTestTool is attached only to the test_case_reviewer agent.&lt;/p&gt;

&lt;p&gt;That means the writer agent is responsible only for generating draft test cases, while the reviewer agent handles the final refinement and the publishing step into Xray. This is a clean separation of responsibilities and helps keep the workflow easier to reason about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use Process.sequential
&lt;/h3&gt;

&lt;p&gt;The crew is configured with Process.sequential, which means the tasks run in order rather than in parallel.&lt;/p&gt;

&lt;p&gt;That makes sense for this use case because the second task depends on the output of the first one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, the writer creates the initial test cases&lt;/li&gt;
&lt;li&gt;then, the reviewer improves them&lt;/li&gt;
&lt;li&gt;finally, the reviewer uses the Xray tool to create Manual Test issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sequential flow is the simplest and safest orchestration model for this kind of staged QA pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this file matters
&lt;/h3&gt;

&lt;p&gt;If agents.yaml defines who the agents are, and tasks.yaml defines what they do, then crew.py defines how everything comes together into one executable system.&lt;/p&gt;

&lt;p&gt;It is the file that turns configuration into a working CrewAI pipeline.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll look at main.py, where the Jira issue is fetched, prepared as input, and passed into the crew for execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 4 — Running the Workflow from main.py
&lt;/h3&gt;

&lt;p&gt;With the agents, tasks, and crew now defined, the final step is to run the workflow with a real Jira issue. In this project, that logic lives in main.py.&lt;/p&gt;

&lt;p&gt;This file acts as the entry point of the application. It loads environment variables, fetches a Jira issue, extracts the story content into a format suitable for the agents, and then starts the CrewAI workflow.&lt;/p&gt;

&lt;p&gt;The key parts of main.py are shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;warnings&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPBasicAuth&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;testcrew_ai.crew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCrewAI&lt;/span&gt;

&lt;span class="n"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterwarnings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;SyntaxWarning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pysbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load .env variables
&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;JIRA_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JIRA_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;JIRA_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JIRA_EMAIL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;JIRA_API_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JIRA_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_jira_issue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Fetch a Jira issue by key.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JIRA_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/rest/api/3/issue/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;HTTPBasicAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JIRA_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JIRA_API_TOKEN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_story_for_ai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Extracts a formatted story from a Jira issue for AI processing.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;description_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No description provided&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;description_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Title:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Description:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;description_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Issue Type:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issuetype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Priority:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Run the research crew for a given Jira issue key.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;issue_key&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;issue_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JIRA_ISSUE_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SCRUM-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_jira_issue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;story_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_story_for_ai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;story&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;story_text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestCrewAI&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;crew&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;kickoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; __main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What main.py does
&lt;/h3&gt;

&lt;p&gt;This file is responsible for preparing the real-world input that the agents will use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It loads Jira credentials from .env&lt;/li&gt;
&lt;li&gt;It fetches a Jira issue using the Jira REST API&lt;/li&gt;
&lt;li&gt;It extracts useful fields such as title, description, issue type, and priority&lt;/li&gt;
&lt;li&gt;It converts that data into a structured text prompt&lt;/li&gt;
&lt;li&gt;It passes that prompt into the CrewAI workflow as the story input&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why the story extraction step matters
&lt;/h3&gt;

&lt;p&gt;The agents do not work directly with raw Jira JSON. Instead, extract_story_for_ai() transforms the issue into a cleaner, readable text format.&lt;/p&gt;

&lt;p&gt;That is important because LLMs perform much better when given structured natural language rather than deeply nested API responses. By extracting only the fields that matter, the project gives the agents a clearer understanding of the requirement they need to convert into test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the execution flow works
&lt;/h3&gt;

&lt;p&gt;At runtime, the flow is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Jira issue key is provided, either directly or through the .env file&lt;/li&gt;
&lt;li&gt;the issue is fetched from Jira&lt;/li&gt;
&lt;li&gt;the story is formatted for AI processing&lt;/li&gt;
&lt;li&gt;the crew is kicked off with that story as input&lt;/li&gt;
&lt;li&gt;the tasks run in sequence until the final reviewed test cases are created and sent to Xray&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes main.py the handoff point between Jira data and the CrewAI pipeline.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll look at the custom xray_tool.py implementation, which is responsible for creating Manual Test issues in Xray from the final reviewed output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 5 — Creating Manual Tests in Xray with xray_tool.py
&lt;/h3&gt;

&lt;p&gt;The final piece of the workflow is the custom tool that sends the reviewed test cases into Xray. This logic lives in tools/xray_tool.py.&lt;/p&gt;

&lt;p&gt;While the agents and tasks handle the AI-driven parts of the pipeline, this tool handles the external integration. Its job is to authenticate with Xray Cloud and create Manual Test issues in the target Jira project.&lt;/p&gt;

&lt;p&gt;Here is the implementation used in this project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseTool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_xray_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Loads and returns Xray credentials from environment variables.
    Raises an exception if required variables are missing.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XRAY_CLIENT_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XRAY_CLIENT_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XRAY_CLIENT_ID and XRAY_CLIENT_SECRET must be set in environment.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_xray_token&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Authenticates with Xray and returns a bearer token.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_xray_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://xray.cloud.getxray.app/api/v2/authenticate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XrayCreateTestTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseTool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Create Xray Test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creates a Manual Test in Xray Jira project&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SCRUM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Creates a manual test in the specified Xray Jira project.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_xray_token&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xray_testtype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Manual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;project&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;project_key&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issuetype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://xray.cloud.getxray.app/api/v2/import/test/bulk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How the tool works
&lt;/h3&gt;

&lt;p&gt;This tool performs two main steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It authenticates with Xray Cloud using XRAY_CLIENT_ID and XRAY_CLIENT_SECRET&lt;/li&gt;
&lt;li&gt;It sends a request to the Xray bulk test import API to create a Manual Test issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The authentication step returns a bearer token, which is then included in the request headers for the test creation call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this tool matters
&lt;/h3&gt;

&lt;p&gt;This is the point where the workflow moves beyond text generation and becomes actionable.&lt;/p&gt;

&lt;p&gt;Up to this stage, the agents are producing and refining Markdown test cases. But with XrayCreateTestTool, the final output is pushed directly into Xray as real Jira Test issues. That makes the system much more useful in practice, because the generated tests are no longer just suggestions, they become part of the team’s test management workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it connects to the review task
&lt;/h3&gt;

&lt;p&gt;Earlier, in tasks.yaml, the reviewer task instructed the agent to call XrayCreateTestTool after producing the final improved test cases.&lt;/p&gt;

&lt;p&gt;That means the reviewer agent is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validating the quality of the final test cases&lt;/li&gt;
&lt;li&gt;selecting the finalized content&lt;/li&gt;
&lt;li&gt;sending each approved test case to Xray&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a nice design because it keeps test publication tied to the quality-control stage rather than the initial draft stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the full Jira-to-Xray workflow
&lt;/h3&gt;

&lt;p&gt;After setting up the project and configuring your credentials, run:&lt;/p&gt;

&lt;p&gt;crewai run&lt;/p&gt;

&lt;p&gt;This will execute the CrewAI pipeline end to end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fetch the Jira story&lt;/li&gt;
&lt;li&gt;generate the first draft of test cases&lt;/li&gt;
&lt;li&gt;review and improve the test cases&lt;/li&gt;
&lt;li&gt;create Manual Test issues in Xray&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The screenshot below shows the project running through the CrewAI pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp9jsctfwq2451vxidt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp9jsctfwq2451vxidt1.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;At this point, the full workflow is in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jira provides the source requirement&lt;/li&gt;
&lt;li&gt;the writer agent generates initial test cases&lt;/li&gt;
&lt;li&gt;the reviewer agent improves them&lt;/li&gt;
&lt;li&gt;the custom Xray tool creates Manual Test issues in Jira&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these pieces form an end-to-end pipeline that turns Jira stories into structured, reviewable, and publishable Xray tests using CrewAI.&lt;/p&gt;

&lt;p&gt;Read part 1 — &lt;a href="https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-1-setup-and-project-overview-p0a"&gt;https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-1-setup-and-project-overview-p0a&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>jira</category>
      <category>python</category>
    </item>
    <item>
      <title>AI-Powered Test Generation: Jira to Xray with CrewAI — Part 1: Setup and Project Overview</title>
      <dc:creator>Abdul Qadir</dc:creator>
      <pubDate>Wed, 18 Mar 2026 01:41:02 +0000</pubDate>
      <link>https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-1-setup-and-project-overview-p0a</link>
      <guid>https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-1-setup-and-project-overview-p0a</guid>
      <description>&lt;p&gt;Discover how CrewAI agents can automatically convert Jira requirements into Xray test cases. Includes full setup instructions, project layout, and a ready-to-run GitHub example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vpuhm8zahaumba7nqhv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vpuhm8zahaumba7nqhv.jpeg" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Before starting this tutorial, make sure you have completed the following setup steps. Each guide walks you through the process from scratch.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These credentials and accounts are required for the CrewAI agents to interact with Jira and Xray.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Required Accounts &amp;amp; API Access&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;OpenAI API Access&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@aqadir9456/how-to-create-an-openai-account-generate-an-api-key-and-add-credits-5b0b2ea2e400" rel="noopener noreferrer"&gt;How to Create an OpenAI Account, Generate an API Key, and Add Credits | by Abdul Qadir | Mar, 2026 | Medium&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jira Test Account&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@aqadir9456/how-to-set-up-a-jira-test-account-and-generate-an-api-key-9db6494bba88" rel="noopener noreferrer"&gt;How to Set Up a Jira Test Account and Generate an API Key | by Abdul Qadir | Mar, 2026 | Medium&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Xray for Jira Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@aqadir9456/how-to-set-up-xray-for-jira-and-generate-client-id-and-client-secret-8b89b4f3ec4d" rel="noopener noreferrer"&gt;How to Set Up Xray for Jira and Generate Client ID and Client Secret | by Abdul Qadir | Mar, 2026 | Medium&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why We Are Using CrewAI?
&lt;/h3&gt;

&lt;p&gt;There are many agent frameworks available today, but CrewAI is a great choice for this project, especially if you’re new to building AI agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Advantages
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Beginner-friendly&lt;/strong&gt;  — simple concepts and minimal boilerplate&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role-based agents&lt;/strong&gt;  — define agents with clear goals (e.g., writer, reviewer)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML configuration&lt;/strong&gt;  — behavior can be defined without heavy coding&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fast to prototype&lt;/strong&gt;  — go from idea to working multi-agent system quickly&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;  — easier to understand than more complex orchestration frameworks&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active community&lt;/strong&gt;  — growing ecosystem and documentation&lt;/p&gt;

&lt;p&gt;In short, CrewAI lets you focus on &lt;em&gt;what the agents should do&lt;/em&gt;, not on building infrastructure.&lt;/p&gt;

&lt;p&gt;For a project like converting Jira stories into Xray tests, this simplicity makes development faster and much easier for newcomers&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Section 1 — Clone the Repository&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Start by cloning the public project repository to your machine. This repo contains the full CrewAI project, configuration files, and example code used in this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install Git (if not already installed)
&lt;/h3&gt;

&lt;p&gt;Check if Git is installed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows / macOS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;git --version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a version number, you’re good to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clone the repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/qadir-dev-hub/ai-xray-test-generator" rel="noopener noreferrer"&gt;qadir-dev-hub/ai-xray-test-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows (PowerShell)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;git clone qadir-dev-hub/ai-xray-test-generator
cd testcrew_ai
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;macOS / Linux (Terminal)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;git clone qadir-dev-hub/ai-xray-test-generator
cd testcrew_ai
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create your environment file
&lt;/h3&gt;

&lt;p&gt;Copy the example environment file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;copy&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;macOS / Linux&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open .env and fill in your credentials (Xray, Jira, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Section 2 — Install uv and Project Dependencies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This project uses uv to manage Python environments and dependencies.&lt;/p&gt;

&lt;p&gt;uv automatically creates a virtual environment and installs everything defined in pyproject.toml.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install uv
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Windows (PowerShell)
&lt;/h3&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ByPass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"irm https://astral.sh/uv/install.ps1 | iex"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will:&lt;/p&gt;

&lt;p&gt;Install uv for your user&lt;br&gt;&lt;br&gt;
Add it to your PATH&lt;br&gt;&lt;br&gt;
Require no admin privileges&lt;/p&gt;
&lt;h3&gt;
  
  
  macOS / Linux
&lt;/h3&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify installation
&lt;/h3&gt;

&lt;p&gt;Close and reopen your terminal, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;uv --version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a version number appears, installation succeeded&lt;/p&gt;

&lt;p&gt;If uv is not recognized, restart your terminal or log out and back in so the PATH update takes effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install project dependencies
&lt;/h3&gt;

&lt;p&gt;Make sure you are in the project root (the folder containing pyproject.toml), then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;uv sync
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will:&lt;/p&gt;

&lt;p&gt;Create a .venv virtual environment&lt;br&gt;&lt;br&gt;
Install CrewAI and all required packages&lt;br&gt;&lt;br&gt;
Generate a uv.lock file for reproducible installs&lt;/p&gt;
&lt;h3&gt;
  
  
  Quick sanity check
&lt;/h3&gt;

&lt;p&gt;If everything installed correctly, you can test Python execution inside the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;uv run python --version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see Python 3.10–3.13 (as defined in the project requirements).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Architecture Diagram&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r87zd77wtp5ctvzv66r.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r87zd77wtp5ctvzv66r.jpeg" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This diagram shows how a Jira user story is transformed into Xray manual tests using AI agents built with CrewAI.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user story from Jira serves as the input.&lt;/li&gt;
&lt;li&gt;CrewAI orchestrates the workflow between agents.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Test Case Writer Agent&lt;/strong&gt; generates initial test cases.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Test Case Reviewer Agent&lt;/strong&gt; refines and finalizes them.&lt;/li&gt;
&lt;li&gt;A custom tool calls the Xray API to create tests.&lt;/li&gt;
&lt;li&gt;Xray saves the manual Test issues back in Jira.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short, the pipeline automates the flow from &lt;strong&gt;requirements → AI-generated test cases → ready-to-use Xray tests&lt;/strong&gt; , significantly reducing manual effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 3 — Project Structure Overview
&lt;/h3&gt;

&lt;p&gt;Now that the project is installed, let’s take a quick look at how the repository is organized.&lt;/p&gt;

&lt;p&gt;Understanding the project structure will make it much easier to follow how CrewAI agents, tasks, and tools work together to generate Xray test cases from Jira requirements.&lt;/p&gt;

&lt;p&gt;A simplified view of the repository looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;testcrew_ai/
├── src/
│ └── testcrew_ai/
│ ├── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ ├── tools/
│ │ └── xray_tool.py
│ ├── crew.py
│ └── main.py
├── .env
├── pyproject.toml
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What each part does
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;config/agents.yaml&lt;/strong&gt;
Defines the AI agents used in the workflow, including their roles, goals, and behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;config/tasks.yaml&lt;/strong&gt;
Describes the tasks assigned to each agent, such as generating and reviewing test cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tools/xray_tool.py&lt;/strong&gt;
Contains the custom tool responsible for connecting to the Xray API and creating manual test cases in Jira.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;crew.py&lt;/strong&gt;
Brings the agents, tasks, and tools together into a CrewAI workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;main.py&lt;/strong&gt;
Serves as the entry point for running the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.env&lt;/strong&gt;
Stores the API keys and credentials required for OpenAI, Jira, and Xray.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pyproject.toml&lt;/strong&gt;
Manages project metadata and dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this structure works well
&lt;/h3&gt;

&lt;p&gt;This layout keeps the project clean and easy to extend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration is separated from code&lt;/li&gt;
&lt;li&gt;Agents and tasks are easy to update without rewriting logic&lt;/li&gt;
&lt;li&gt;External integrations are isolated inside reusable tools&lt;/li&gt;
&lt;li&gt;The execution flow stays simple and readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation is one of the reasons CrewAI is a good fit for projects like this. You can clearly see where the agent behavior lives, where the business logic lives, and where external systems like Xray are connected.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll look at how the agents are defined and how each one contributes to the test generation workflow and how these agents are assigned work through tasks.yaml, and how the overall process moves from Jira requirements to Xray-ready test cases.&lt;/p&gt;

&lt;p&gt;Read part 2 here — &lt;a href="https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-2-agents-tasks-and-xray-integration-3jid"&gt;https://dev.to/abdul_qadir/ai-powered-test-generation-jira-to-xray-with-crewai-part-2-agents-tasks-and-xray-integration-3jid&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>jira</category>
      <category>automation</category>
    </item>
    <item>
      <title>Getting Started with Xray for Jira: Setup, Test Configuration, and API Keys</title>
      <dc:creator>Abdul Qadir</dc:creator>
      <pubDate>Sun, 15 Mar 2026 16:10:55 +0000</pubDate>
      <link>https://dev.to/abdul_qadir/getting-started-with-xray-for-jira-setup-test-configuration-and-api-keys-25ml</link>
      <guid>https://dev.to/abdul_qadir/getting-started-with-xray-for-jira-setup-test-configuration-and-api-keys-25ml</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn3pim7zc42q9qo5riyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn3pim7zc42q9qo5riyd.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building test automation, integrating CI/CD pipelines, or working with AI-driven QA workflows, setting up Xray correctly is the first step.&lt;/p&gt;

&lt;p&gt;This guide walks you through the full setup—from installation to generating API credentials you can use in automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Xray?
&lt;/h2&gt;

&lt;p&gt;Xray Test Management is a powerful test management app that integrates directly with Jira. It allows teams to manage manual and automated tests, track execution results, and connect testing to requirements and defects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Install Xray in Jira
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Jira Cloud instance
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Settings&lt;/strong&gt; icon (gear)
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Apps&lt;/strong&gt; or &lt;strong&gt;Manage apps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Find new apps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Xray Test Management&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Try it free&lt;/strong&gt; or &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After installation, Xray will be added to your Jira environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You must be a Jira administrator to install apps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihityg8fr10eyfes44ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihityg8fr10eyfes44ue.png" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Create a Test Issue Type in Jira
&lt;/h2&gt;

&lt;p&gt;Xray stores test cases as a special issue type called &lt;strong&gt;Test&lt;/strong&gt;. If your Jira instance does not already have this issue type, you must create it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Space Settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add work type&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Name it &lt;strong&gt;Test&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select an icon and click &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2a4lv8mbrqydm4tmrar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2a4lv8mbrqydm4tmrar.png" width="800" height="926"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Link the Jira Test Issue Type to Xray
&lt;/h2&gt;

&lt;p&gt;After creating the Test issue type, ensure Xray recognizes it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Jira Settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Apps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Xray settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Locate &lt;strong&gt;Issue Type Mapping&lt;/strong&gt; or &lt;strong&gt;Test Issue Configuration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Confirm that the &lt;strong&gt;Test&lt;/strong&gt; issue type is mapped as an Xray Test
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows Xray to treat those issues as test cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthh4ayfgv9ql3upyq91v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthh4ayfgv9ql3upyq91v.png" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7ndbmwtmk2a79m3r0zo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7ndbmwtmk2a79m3r0zo.png" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Generate Client ID and Client Secret
&lt;/h2&gt;

&lt;p&gt;To use Xray’s REST API (for automation tools or scripts), you need authentication credentials.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Xray settings, find the &lt;strong&gt;API Keys&lt;/strong&gt; or &lt;strong&gt;API Access&lt;/strong&gt; section
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create API Key&lt;/strong&gt; or &lt;strong&gt;Generate Credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Client ID&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Client Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Store both values securely
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The Client Secret is sensitive and may not be shown again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbunybwwsdsk5r8ubap9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbunybwwsdsk5r8ubap9l.png" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Use the Credentials (API Example)
&lt;/h2&gt;

&lt;p&gt;Once you have your credentials, you can authenticate with Xray and start using its APIs.&lt;/p&gt;

&lt;p&gt;Here’s a simple example to get an authentication token:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
curl -X POST "https://xray.cloud.getxray.app/api/v2/authenticate" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>qa</category>
      <category>testing</category>
      <category>jira</category>
      <category>automation</category>
    </item>
    <item>
      <title>Jira API Token Guide: Create a Test Account and Generate API Keys (2026)</title>
      <dc:creator>Abdul Qadir</dc:creator>
      <pubDate>Sun, 15 Mar 2026 02:39:27 +0000</pubDate>
      <link>https://dev.to/abdul_qadir/jira-api-token-guide-create-a-test-account-and-generate-api-keys-2026-1fm8</link>
      <guid>https://dev.to/abdul_qadir/jira-api-token-guide-create-a-test-account-and-generate-api-keys-2026-1fm8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jy39jesq06sy50ejpz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jy39jesq06sy50ejpz5.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building integrations, test automation, or AI-driven workflows with Jira, you'll need an API token to authenticate your requests.&lt;/p&gt;

&lt;p&gt;This guide shows how to quickly create a Jira test account and generate an API token you can use in scripts, CI/CD pipelines, or automation tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Create a Jira Account
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;https://www.atlassian.com/software/jira&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Get it free&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sign up using your email, Google account, or Microsoft account
&lt;/li&gt;
&lt;li&gt;Follow the prompts to create your Jira site (e.g. &lt;code&gt;yourname.atlassian.net&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Once complete, you’ll have access to a free Jira Cloud instance
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This environment is perfect for testing integrations and automation safely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Generate an API Token
&lt;/h2&gt;

&lt;p&gt;An API token allows scripts and tools to authenticate securely without using your password.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to: &lt;a href="https://id.atlassian.com/manage-profile/security/api-tokens" rel="noopener noreferrer"&gt;https://id.atlassian.com/manage-profile/security/api-tokens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create API token&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter a label (e.g. &lt;em&gt;Jira Automation&lt;/em&gt;)
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the token and store it securely
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; You won’t be able to view the token again after closing the dialog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpm1vgtzaazp5ze8iw4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpm1vgtzaazp5ze8iw4z.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Use the API Token (Example)
&lt;/h2&gt;

&lt;p&gt;Most tools authenticate to Jira Cloud using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username&lt;/strong&gt; → your Atlassian email
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password&lt;/strong&gt; → your API token
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simple example using &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
curl -u your-email@example.com:YOUR_API_TOKEN \
  -X GET \
  -H "Accept: application/json" \
  https://your-domain.atlassian.net/rest/api/3/project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>qa</category>
      <category>testing</category>
      <category>jira</category>
      <category>automation</category>
    </item>
    <item>
      <title>OpenAI API Quick Start (2026): Account, API Key, and Billing Setup</title>
      <dc:creator>Abdul Qadir</dc:creator>
      <pubDate>Fri, 13 Mar 2026 21:24:46 +0000</pubDate>
      <link>https://dev.to/abdul_qadir/openai-api-quick-start-2026-account-api-key-and-billing-setup-9b8</link>
      <guid>https://dev.to/abdul_qadir/openai-api-quick-start-2026-account-api-key-and-billing-setup-9b8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahas3vu3bpze4ha0ebg0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahas3vu3bpze4ha0ebg0.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If you want to start experimenting with OpenAI APIs, here’s a quick guide to get you set up. This covers account creation, generating an API key, and adding a small prepaid balance, perfect if you just want to test things out.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building apps, automations, or AI agents, this is the fastest way to get your OpenAI API access ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create your OpenAI account
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://auth.openai.com/" rel="noopener noreferrer"&gt;https://auth.openai.com/&lt;/a&gt; and click &lt;strong&gt;Sign up&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can create an account using your email address, or sign in with Google or Microsoft. After entering your details, you’ll need to verify your email and complete a few basic prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Generate an API key
&lt;/h3&gt;

&lt;p&gt;Once your account is ready, go to &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;https://platform.openai.com/&lt;/a&gt; and sign in.&lt;/p&gt;

&lt;p&gt;Open the Settings menu and navigate to &lt;strong&gt;API Keys&lt;/strong&gt;. Click &lt;strong&gt;Create new secret key&lt;/strong&gt; , then copy the key immediately. You won’t be able to see it again after you close the dialog.&lt;/p&gt;

&lt;p&gt;Treat this key like a password. Anyone with access to it can use your credits.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add credits to your account
&lt;/h3&gt;

&lt;p&gt;To use the API, you need a prepaid balance.&lt;/p&gt;

&lt;p&gt;Go to the billing section at:&lt;br&gt;&lt;br&gt;
 &lt;a href="https://platform.openai.com/settings/organization/billing/overview?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;https://platform.openai.com/settings/organization/billing/overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add credit&lt;/strong&gt; , enter your payment details, and choose an amount. The minimum top‑up is typically $5.&lt;/p&gt;

&lt;p&gt;After confirming, your balance will be available for API usage. You may see a temporary authorization charge on your card, which is normal and usually reversed automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F609ngie1j7kg9ksz5z4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F609ngie1j7kg9ksz5z4u.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does $5 last?
&lt;/h3&gt;

&lt;p&gt;For simple testing, short prompts and occasional requests , a $5 credit can last for months. It depends entirely on how often you use the API and which models you call.&lt;/p&gt;

&lt;p&gt;If you’re just learning, building small demos, or experimenting locally, $5 is usually more than enough to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor your usage
&lt;/h3&gt;

&lt;p&gt;You can monitor your API usage and track how quickly your credits are being spent here:&lt;br&gt;&lt;br&gt;
 &lt;a href="https://platform.openai.com/settings/organization/usage" rel="noopener noreferrer"&gt;https://platform.openai.com/settings/organization/usage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is especially helpful if you’re testing with limited credits or want to keep your usage under control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n0i3yrgewlqzhf5b3ja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n0i3yrgewlqzhf5b3ja.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic security tips
&lt;/h3&gt;

&lt;p&gt;Do not share your API key publicly or commit it to source control.&lt;/p&gt;

&lt;p&gt;Use environment variables or a secure secrets manager instead of hardcoding the key in your code.&lt;/p&gt;

&lt;p&gt;If you think your key has been exposed, delete it and generate a new one immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;That’s all you need to start using OpenAI’s APIs. The entire process takes only a few minutes, and a small prepaid balance is enough for most beginner experiments.&lt;/p&gt;

&lt;p&gt;Once you’re set up, you can begin building apps, automations, or integrations powered by AI.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>webdev</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
