File issues from CI with the Radial CLI and --json
Turn a failed CI job into a filed issue automatically. Install the radial CLI, run radial create with --json, and pipe the returned RAD-123 identifier back into your pipeline: no dashboard, no seat cost.
A failed CI job already knows everything a good issue needs: which test broke, on which commit, in which job, with a link to the logs. The problem is that this information dies in a pipeline log unless someone reads the red X, copies the details into your tracker by hand, and assigns it. That someone usually forgets, and the flake comes back next week as a surprise.
The fix is to let the pipeline file the issue itself. With the radial CLI, that is one command: radial create, with --json so your script can read back the identifier of the issue it just filed. No browser, no dashboard, no human in the loop, and because every automation credential is an agent that rides free, no seat cost for the CI job doing the filing.
#The one command
Here is the whole move. When a job fails, create an issue titled after the failure, tagged to the right team and priority, and capture the returned identifier:
# In a CI job that runs on failure. RADIAL_API_KEY is a scoped key stored as a secret.
issue=$(radial create "CI failed on ${CI_COMMIT_SHORT_SHA}: ${FAILED_JOB}" \
--team ENG \
--priority high \
--label ci \
--description "Job ${CI_JOB_URL} failed on ${CI_COMMIT_BRANCH}. Logs attached above." \
--json)
# --json returns the created issue as an object; read the human-readable id back out.
echo "Filed $(echo "$issue" | jq -r '.id') — $(echo "$issue" | jq -r '.url')"The --json flag is the part that makes this scriptable. Every radial command takes it, and for create it emits the created issue as a single JSON object. The field you almost always want is .id, the human-readable identifier like RAD-412, so your pipeline can print it, post it to a chat, or hand it to the next step. There is also .url if you want to link straight to the issue, and .status, .priority, .team if you want to assert what you filed. These are real fields on the response, not invented ones: the CLI speaks the same human-readable shape everywhere, so you get RAD-412 and ENG back, never a raw UUID.
#Why the CLI and not raw REST
You can absolutely do this against the REST API with curl, and for some pipelines that is the right call. But the CLI buys you three things that matter inside CI:
- One binary, no request plumbing.
npm i -g radial.buildorbrew install BrainGridAI/radial/radialand you have the command. You are not hand-rolling headers, JSON bodies, and error handling in bash. - Human-readable in, human-readable out. You pass
--team ENGand--priority high, not internal ids you had to look up first. The response comes back withRAD-412and status names, so the identifier you print is the identifier a human recognizes. --jsonon every command. The same flag that makescreatescriptable also makeslist,show,search, andclosescriptable, so the CLI composes. A cleanup job canradial search "CI failed" --jsonto find yesterday's flakes andradial closethe ones that a rerun already fixed.
The CLI is a thin, honest wrapper over the same REST surface. Reach for curl when you are already deep in a language runtime and adding a dependency is a hassle; reach for the CLI when you want the shortest correct line in a shell script.
#The credential: a scoped key, stored as a secret
CI is non-interactive, so there is no browser to click "approve" in. That means you authenticate with a scoped API key, not OAuth. Mint one key for this job, give it exactly the access it needs, and store it as a CI secret:
radial keys create "ci-bot" --scope writeThe key comes back once, prefixed rk_, and you paste it into your CI provider's secret store as RADIAL_API_KEY. If a CI job only ever files bugs and never closes them, you could even scope it to something narrower, but filing needs write, so write is the honest scope here. The important part is that this is one key for one job: if it ever leaks, you revoke that one credential and leave the rest of your automation running. That is the whole argument for one scoped key per agent.
And the key provisions its own agent identity in the workspace, which never counts as a billed seat. Your CI bot is not a $50 user. The humans on the team are the seats; the pipeline is a free client of the API. There is no per-agent charge and no AI credit meter anywhere in the product, which is the Plain Software Pledge in practice: the day Radial ships a copilot, meters your usage, or charges you for AI you didn't ask for, your subscription is free.
#Wiring it into a pipeline
The pattern is provider-agnostic, because it is just a shell command reading environment variables your CI already sets. In GitLab CI it is an after_script or a job with when: on_failure; in GitHub Actions it is a step with if: failure(); in a Jenkins pipeline it is a post { failure { ... } } block. In every case the shape is the same:
- The job fails.
- A failure hook runs
radial createwith the commit, branch, and job URL pulled from the CI environment variables. --jsonreturns the new issue; you capture.idand.urland print or post them.
Because create is idempotent only if you make it so, a naive setup will file a fresh issue every time a flaky job fails. If that is noisy for you, add a radial search --json guard before creating: look for an open issue with the same title, and only radial create when there is not one already. The CLI composes precisely because every command speaks --json.
#FAQ
#How do I create an issue from a CI pipeline automatically?
Run radial create inside a failure hook in your pipeline (GitLab when: on_failure, GitHub Actions if: failure(), Jenkins post { failure }). Pass the title, team, priority, and a description built from your CI environment variables, and add --json so the pipeline can read back the created issue's identifier. Authenticate with a scoped rk_ API key stored as a CI secret. That is the entire integration: no plugin, no webhook server, just a CLI command in a hook.
#How do I get the new issue's ID back in my script?
Add --json to radial create. It prints the created issue as a JSON object, and the human-readable identifier is on the .id field (for example RAD-412). Pipe it through jq -r '.id' to capture just the identifier, or read .url to link straight to the issue. Every field the CLI returns is human-readable by design, so you get RAD-412 and ENG, not UUIDs.
#Does a CI bot filing issues cost a seat?
No. The API key you mint for the job provisions an agent member, and agent members never count as billed seats. You pay the flat $50 per user, per year, billed annually for the humans on the workspace, and that number does not move when you add a CI bot, a triage agent, or a whole fleet of them. Agents ride free.
#How do I avoid filing a duplicate issue every time a flaky job fails?
Guard the create with a search. Run radial search "<the failure title>" --json first, and only call radial create when it returns no open match. Because every radial command takes --json, the search result is machine-readable and easy to test in a shell condition. Import is single-run and create is not deduplicated for you, so this guard is how you keep the tracker from filling up with copies of the same flake.
#Can my coding agent do this instead of a shell script?
Yes. If an interactive agent like Claude Code is already connected over the MCP server, it can call create_issue directly when it notices a failing job, no CLI required. The CLI path is for the non-interactive case: a pipeline running headless with no human at the browser. Same tracker, same free agent identity, two ways in.
#The short version
A red pipeline should file its own issue. Install the CLI, mint one scoped write key, and run radial create ... --json from a failure hook, then read .id back out to link the issue you just filed. The CI bot is a free agent, the identifier that comes back is one a human recognizes, and nothing about it touches a meter.
See the full CLI and API surface on the developers page, or read how scoped API keys let you give each automation exactly the access it needs.
The team behind Radial, the fast, CLI-first issue tracker that lets your own agents work for free. We write about plain software, speed as respect, and bringing your own agent.
Track issues like it’s 2019. Ship like it’s 2026.
An issue tracker. That’s it. Your agents ride free.
Keep reading
Scoped API keys: one key per agent, read-only, triage-only, or full
Give each agent its own Radial API key with exactly the access it needs: read-only, triage-only, or full write. Least privilege for an agent fleet, plus how to mint, scope, and revoke keys from the CLI.
What is an MCP server? And how to point one at your issue tracker
An MCP server is the thing an AI agent connects to so it can use tools and data outside its own context. Here is what that means, how it differs from a plain API, and how to point one at your issue tracker so your agent files and closes work itself.
Give Claude Code access to your issue tracker in 5 minutes (MCP)
Wire Claude Code to your issue tracker over MCP so your agent files, searches, and closes issues itself. A 5-minute setup, the OAuth flow, and why the durable record belongs where the agent already writes.