Jira
Connect Jira to Tuneloop and your AI coding spend becomes answerable per ticket: what did AL-142 cost us, and did it ship?
What You Get
| Surface | Question it answers |
|---|---|
| Tickets linked to sessions | See all sessions that contributed to a ticket: status, story points, cost |
| Cost per shipped ticket | Where the money went, rolled up epic → story → subtask, trended over time |
| Engineering productivity metrics | Feature velocity sliced by AI dimensions — model, harness, complexity, autonomy |
Ticket cost slices by team and by user, so a team lead sees their team's spend on a shared ticket and an admin sees the total with a per-team breakdown.
Setup
You need Jira admin rights and Tuneloop admin rights. Everything happens in Settings → Connectors → Jira.
1. Site URL
Enter your Jira origin — https://your-site.atlassian.net — and save.
2. API Token
Create a Jira API token for a service account, not a personal one. Enter that account's email and the token, then save.
This lets Tuneloop fill in ticket titles, statuses, and story points. Read-only access is enough.
3. Webhook
Generate the webhook secret in the panel and copy the URL it shows you.
In Jira: Settings → System → WebHooks → Create a WebHook. Paste the URL, and under Issue tick created and updated.
A JQL filter is optional. Tuneloop only holds rows for tickets its own sessions worked on — an event for any other key is discarded on arrival. So a noisy webhook costs nothing but the HTTP request, and filtering in Jira only risks silencing an event you did need.
If Tuneloop is deployed behind a firewall, allow inbound traffic from Atlassian's outbound IPs so that webhook events can reach your server. The current IP ranges are published at Atlassian's IP addresses documentation. Not required for self-hosted Jira Server or Data Center.
4. Verify
Change any issue in Jira. The Webhooks row in the panel should read "last event a few seconds ago".
Then reprocess to backfill existing sessions — tickets already named in past transcripts get picked up on the next analysis pass.
How It Works
Tickets enter through sessions, never through a bulk sync
A ticket appears in Tuneloop only when a session is judged to have worked on it. Two things are weighed on every session:
- Keys the session names — the agent called Jira, or the key appeared in a branch or commit. Direct evidence.
- Tickets the session's owner had open at the time — possibilities, weighed against what the session actually did. This catches work that gets attributed when a developer describes a ticket in prose and moves it in the Jira UI afterwards, so the key is never typed anywhere.
Signals a ticket is picked up from
| Signal | Example |
|---|---|
| Ticket created or transitioned | The agent called Jira (MCP or CLI) |
| Ticket read | The agent fetched or viewed the issue |
| Key in a branch name | AL-142-fix-retry |
| Key in a commit message | AL-142: fix the retry path |
| Key in a PR title | Links the PR and the ticket |
| Owner's open ticket, matched to the work | No key anywhere — matched from the session's content |
How cost reaches a ticket
A session is split into blocks — coherent stretches of work. Cost is attributed per block, so a session that touched three tickets isn't charged wholesale to one.
Limits
- Identity is the email address. A user's Tuneloop login is assumed to be their Jira address. A mismatch costs that person's ticket attribution.
- Deleted issues are ignored. A ticket deleted in Jira keeps its last known state rather than being retracted.
Privacy
- Tuneloop reads Jira. It never writes to it.
- Only tickets a session is judged to have worked on are stored — issue key, title, status, type, story points, parent.