This cookbook provides a practical, step-by-step approach to automating the workflow between Jira and GitHub. By labeling a Jira issue, you trigger an end-to-end process that creates a GitHub pull request, keeps both systems updated, and streamlines code review, all with minimal manual effort. The automation is powered by the codex-cli agent running inside a GitHub Action.
The flow is:
Label a Jira issue
Jira Automation calls the GitHub Action
The action spins up codex-cli to implement the change
A PR is opened
Jira is transitioned & annotated - creating a neat, zero-click loop. This includes changing the status of the ticket, adding the PR link and commenting in the ticket with updates.
The first step in this rule listens for changes to an issue’s labels. This ensures we only trigger the automation when a label is added or modified—no need to process every update to the issue.
Next, we check whether the updated labels include a specific keyword, in our example we are using aswe. This acts as a filter so that only issues explicitly tagged for automation proceed, avoiding unnecessary noise from unrelated updates.
If the condition is met, we send a POST request to GitHub’s workflow_dispatch endpoint. This kicks off a GitHub Actions workflow with the relevant issue context. We pass in the issue key, summary, and a cleaned-up version of the description—escaping quotes and newlines so the payload parses correctly in YAML/JSON. There are additional fields available as variables in JIRA to give the codex agent more context during its execution.
This setup allows teams to tightly control which Jira issues trigger automation, and ensures GitHub receives structured, clean metadata to act on. We can also set up multiple labels, each triggering a different GitHub Action. For example, one label could kick off a quick bug fix workflow, while another might start work on refactoring code or generating API stubs.
GitHub Actions enable you to automate workflows within your GitHub repository by defining them in YAML files. These workflows specify a series of jobs and steps to execute. When triggered either manually or via a POST request, GitHub automatically provisions the necessary environment and runs the defined workflow steps.
To process the POST request from JIRA we will create a Github action with a YAML like below in the .github/workflows/ directory of the repository:
name: Codex Automated PRon:workflow_dispatch:inputs:issue_key:description: 'JIRA issue key (e.g., PROJ-123)'required: trueissue_summary:description: 'Brief summary of the issue'required: trueissue_description:description: 'Detailed issue description'required: truepermissions:contents: write# allow the action to push code & open the PRpull-requests: write# allow the action to create and update PRsjobs:codex_auto_pr:runs-on: ubuntu-lateststeps:# 0 – Checkout repository - uses: actions/checkout@v4with:fetch-depth: 0# full history → lets Codex run tests / git blame if needed# 1 – Set up Node.js and Codex - uses: actions/setup-node@v4with:node-version: 22 - run: pnpm add -g @openai/codex# 2 – Export / clean inputs (available via $GITHUB_ENV) - id: varsrun: | echo "ISSUE_KEY=${{ github.event.inputs.issue_key }}" >> $GITHUB_ENV echo "TITLE=${{ github.event.inputs.issue_summary }}" >> $GITHUB_ENV echo "RAW_DESC=${{ github.event.inputs.issue_description }}" >> $GITHUB_ENV DESC_CLEANED=$(echo "${{ github.event.inputs.issue_description }}" | tr '\n' ' ' | sed 's/"/'\''/g') echo "DESC=$DESC_CLEANED" >> $GITHUB_ENV echo "BRANCH=codex/${{ github.event.inputs.issue_key }}" >> $GITHUB_ENV# 3 – Transition Jira issue to "In Progress" - name: Jira – Transition to In Progressenv:ISSUE_KEY: ${{ env.ISSUE_KEY }}JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}run: | curl -sS -X POST \ --url "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions" \ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \ --header 'Content-Type: application/json' \ --data '{"transition":{"id":"21"}}' # 21 is the transition ID for changing the ticket status to In Progress. Learn more here: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-get# 4 – Set Git author for CI commits - run: | git config user.email "github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]"# 5 – Let Codex implement & commit (no push yet) - name: Codex implement & commitenv:OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}CODEX_QUIET_MODE: "1"# suppress chatty logsrun: | set -e codex --approval-mode full-auto --no-terminal --quiet \ "Implement JIRA ticket $ISSUE_KEY: $TITLE. $DESC" git add -A git commit -m "feat($ISSUE_KEY): $TITLE"# 6 – Open (and push) the PR in one go - id: cpruses: peter-evans/create-pull-request@v6with:token: ${{ secrets.GITHUB_TOKEN }}base: mainbranch: ${{ env.BRANCH }}title: "${{ env.TITLE }} (${{ env.ISSUE_KEY }})"body: | Auto-generated by Codex for JIRA **${{ env.ISSUE_KEY }}**. --- ${{ env.DESC }}# 7 – Transition Jira to "In Review" & drop the PR link - name: Jira – Transition to In Review & Comment PR linkenv:ISSUE_KEY: ${{ env.ISSUE_KEY }}JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}PR_URL: ${{ steps.cpr.outputs.pull-request-url }}run: | # Status transition curl -sS -X POST \ --url "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/transitions" \ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \ --header 'Content-Type: application/json' \ --data '{"transition":{"id":"31"}}' # 31 is the Transition ID for changing the ticket status to In Review. Learn more here: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-get # Comment with PR link curl -sS -X POST \ --url "$JIRA_BASE_URL/rest/api/3/issue/$ISSUE_KEY/comment" \ --user "$JIRA_EMAIL:$JIRA_API_TOKEN" \ --header 'Content-Type: application/json' \ --data "{\"body\":{\"type\":\"doc\",\"version\":1,\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"PR created: $PR_URL\"}]}]}}"
You can open the PR link posted in the JIRA ticket and check to see if everything looks good and then merge it. If you have branch protection and Smart Commits integration enabled, the Jira ticket will be automatically closed when the pull request is merged.