Ticket-to-pull-request is the fastest-growing category on the Atlassian Marketplace. Click a button on a Jira issue, a coding agent reads the description, and a pull request shows up a few minutes later. The connectors that do this have tens of thousands of installs between them and, if you read their reviews, the same four complaints: the agent runs on the vendor's infrastructure with access to your repository, GitLab is not supported, the repository has to be typed into a chat box on every request, and the base branch is ignored. Enterprises add a fifth: nobody can say who dispatched what, with whose credentials, against which repo.
None of that is inherent to the idea. Your CI already knows how to check out a branch, run a command with a secret, push, and open a pull request. The only thing missing is a trustworthy way to fire it from a Jira issue and get the result back. This post walks through the do-it- yourself version, where the pieces get awkward, and an in-tenant app that packages the pattern.
Why the hosted connectors have the trust model backwards
- Your code and your API key leave your control. A hosted agent needs read and write access to the repository and a model API key. Both live on the vendor's side, under the vendor's retention and incident policies, not yours.
- GitHub only. Most connectors integrate with GitHub's app model. GitLab shops, which are a large share of regulated engineering teams, are simply locked out.
- Per-request configuration. Because the connector has no per-project memory, the repository and branch get re-stated in the prompt every time, and get wrong every few times.
- No governance surface. There is no cap on runs, no allowlist of projects, and no audit log an admin can export when someone asks how much agent spend a team produced.
The DIY approach: fire your own pipeline from Jira
Everything you need already exists on both sides. On the Jira side, an Automation rule can send
a web request when someone adds a label or transitions an issue. On the CI side, GitHub Actions
exposes the repository_dispatch event and GitLab exposes pipeline trigger tokens.
The recipe, for GitHub:
- Create a fine-grained personal access token with
contents: writeon the target repository, and store it in the Automation rule's web-request step. - In the rule,
POST https://api.github.com/repos/OWNER/REPO/dispatcheswith a body like{"event_type":"agent","client_payload":{"issueKey":"{{issue.key}}","summary":"{{issue.summary}}","description":"{{issue.description}}"}}. - Add a workflow triggered
on: repository_dispatchthat checks out the base branch withpersist-credentials: false, creates a branch named after the issue key, runs the agent non-interactively (for exampleclaude -p "$PROMPT"withANTHROPIC_API_KEYfrom repository secrets, or the Codex CLI with its key), commits, pushes, and runsgh pr create. - Enable "Allow GitHub Actions to create and approve pull requests" under the repository's Actions settings, or the last step fails with "not permitted".
- Report back: a final step calls the Jira REST API to add a comment with the PR link. That needs a Jira API token stored as a second CI secret.
GitLab is the same shape with different verbs: a pipeline trigger token, a POST to
/projects/ID/trigger/pipeline with the issue fields as variables, and a job that
uses glab mr create or the Merge Requests API.
Where the DIY version gets awkward
- Secrets in the wrong place. The GitHub token lives inside a Jira Automation rule, readable by anyone who can edit rules in that project, and a Jira API token lives in CI so the pipeline can comment back. Two long-lived credentials, each on the side that should not hold it.
- One rule per project per repository. The repository, base branch, and provider are baked into the rule. Ten projects means ten rules to keep in sync.
- Status is a comment, at best. There is no queued, running, or failed state on the issue, and a pipeline that dies before its last step leaves the ticket silent forever.
- Prompt injection through the shell. Issue text reaching a
run:step through${{ }}interpolation is a command-injection bug. It has to go through environment variables, and someone has to know that. - No cap, no allowlist, no audit. Any rule editor can wire any project to any repository, and the only record of runs is scattered across Actions logs.
A ready-made option: Agent Dispatch for Jira
Agent Dispatch is the DIY pattern with the awkward parts removed. A Send to agent button in the
issue panel packages the work item and fires the pipeline you configured for that project,
GitHub Actions through repository_dispatch or GitLab CI through a trigger token.
Repository, base branch, provider, and agent are set once in Project settings, so nobody types
them per request, and an existing ISSUE-KEY branch is reused when one exists. Workflow templates
for Claude Code and Codex ship in the admin page: paste one file into the repo and add your
model API key as a CI secret. The pipeline reports back through a per-run, single-use callback
token, so the CI job never holds Jira credentials, and your GitHub or GitLab dispatch credential
sits in Forge secret storage, write-only. Status (queued, running, succeeded, failed) and the PR
or MR link land on the issue as a panel update and a comment, and an hourly reaper marks runs
whose pipeline never reported back as failed instead of stranding them. Admins get a site-wide
daily run cap, a per-project allowlist, and an audit log of every dispatch with CSV export.
Two honest notes. This app has egress by design, so unlike the rest of our portfolio it is not in the Runs on Atlassian program; what leaves your tenant is the summary and description of an issue someone chose to dispatch, sent to the CI you configured. And self-managed GitHub Enterprise Server or self-hosted GitLab are not supported yet, because Forge declares egress per domain.
Key facts
- Hosted connectors: agent runs on the vendor's infrastructure with your repo access and API key; GitHub only; repo re-typed per request.
- DIY route: Jira Automation web request to
repository_dispatchor a GitLab trigger token, a workflow that runs the agent CLI and opens the PR, and a REST call back to Jira. - DIY costs: two long-lived secrets on the wrong sides, one rule per project, no run state on the issue, shell-injection risk from issue text, no cap or audit.
- Durable fix: per-project mapping stored in the tenant, dispatch from the issue, tokened callback, live status and PR link on the ticket, cap and audit CSV for admins.
Related reading: where groups hide in Jira, and how to audit them all.
Claude Code is a trademark of Anthropic, PBC. Codex is a trademark of OpenAI. Names are used only to state compatibility; Katabarwa Labs is not affiliated with Anthropic, OpenAI, GitHub, or GitLab.
Written by Abaho Katabarwa at Katabarwa Labs. We build small, single-purpose Atlassian and Azure tools that run entirely inside your own tenant. Questions: support@llmgraph.ai.