Issue tracker (Jira & Linear)
Connect Jira or Linear and file a report's recommendations as real issues — or take the payloads without connecting anything.
Tickets, with or without a connection#
Every analysis can turn its recommendations into ticket payloads — a title, a written-up body, a priority, and labels — from the Create tickets button on the report. That needs no connection and no credentials: you get provider-neutral text to paste wherever you track work.
Connecting Jira or Linear adds one thing on top: a button that files those issues for real, in your tracker, with links back to the analysis. Generation always happens first and creation is a separate, explicit click — so opening the tickets page can never file anything by itself.
Who can connect one#
Connecting a tracker is on the Fleet plan, and only a workspace owner or admin can do it. A connection is workspace-wide configuration that files issues into someone else's tracker under stored credentials, so it sits with projects and schedules on the manage side of the role boundary, not with everyday analysis.
One connection per provider per workspace. Connecting the same provider again replaces the stored credential rather than adding a second — that's how you rotate an expiring token.
Connecting Jira#
Go to Settings → Issue tracker and choose Jira. Four fields, all required:
| Field | What to enter |
|---|---|
Site URL | Your Jira Cloud site, e.g. https://your-team.atlassian.net. A trailing slash is fine — it gets trimmed. |
Account email | The Atlassian account email that the API token belongs to. Issues are created as this account. |
Project key | The short key of the target project, like PERF. Case doesn't matter; it's folded to upper case to match Jira's own API. |
API token | Create one at id.atlassian.com → Security → API tokens. It is encrypted before storage and never shown again. |
*.atlassian.net). A self-managed Jira Server / Data Center instance on a private network is rejected on purpose — see how credentials are handled.Connecting Linear#
Choose Linear in the same place. Two fields: a API key from Linear → Settings → API → Personal API keys (it starts lin_api_…, is encrypted before storage, and is never shown again), and the Team ID of the team to file into — either the team's UUID or its short key.
If a workspace connects both providers, Linear wins: issues are filed there. Disconnect Linear to fall back to Jira.
Saving checks the credentials for real#
Pressing Connect doesn't just store what you typed — it calls the provider first, and stores nothing unless the call succeeds. For Jira that's two checks, because they fail for different reasons and you need to know which:
- Sign-in check — proves the email and API token authenticate at all. A failure reads “Jira rejected that email and API token.”
- Project check — proves the project key exists and is visible to that account. A failure reads “Jira project "PERF" was not found, or this account can't see it.”
Linear is checked the same way in one call: the key must resolve a viewer, and the team id must resolve a team. Once verified, the card shows a plain identity label — your-team.atlassian.net · PERF, or the Linear team's name — so you can tell at a glance which project you're filing into.
What gets filed#
Open any analysis and press Create tickets: the page lists one draft per recommendation. Every draft is editable — rewrite the title inline, expand a row to edit its body — and every row has a checkbox, so you file the two you want rather than all seven. Only Highest & High and Only low-effort narrow the selection in one click. Your edits are what get filed. Each issue carries the recommendation's rationale, its expected gain and effort, and a link back to the analysis.
The bar at the bottom files the selection, one ticket at a time — that is the point: tickets succeed or fail independently, so a row a provider rejects shows its reason while every other row keeps its result, and Stop after current halts a batch mid-flight. Once a ticket has been filed its row locks and shows the issue key and a link, so nothing gets filed twice; the button then offers to retry only what failed.
Report priority maps onto each provider's own scale:
| Harpoon | Jira | Linear |
|---|---|---|
P0 | Highest | Urgent |
P1 | High | High |
P2 | Medium | Medium |
P3 | Low | Low |
- Jira — filed with labels
performance,effort-…andpriority-…. The issue type is read from your project rather than assumed: aTaskif you have one, else aStory, else the first type the project will actually accept — so this works on team-managed, company-managed and service projects alike. The body is converted to Atlassian Document Format, so headings, bullets, bold and links render properly rather than arriving as raw markdown. - Linear — created in the connected team with the mapped priority; Linear renders the markdown body natively.
- Each ticket also carries a ready-to-paste prompt for a coding agent, built from the engine's own finding text and measurements. It is assembled deterministically — no model call, and no raw capture.
Retry; the payload is always there to copy by hand as a fallback.When your project requires extra fields#
Some Jira projects require custom fields on their create screen — a risk assessment, a cost centre, a business unit. Harpoon can't invent a value for one of those, so Jira rejects the issue with something like customfield_14380: RIA is required.
Connecting a project checks for this, and the Jira card in Settings → Issue tracker lists any such field with an input for it: a dropdown of the permitted options when Jira publishes them, free text otherwise. Set a value once and every issue Harpoon files carries it. The field's id is shown under each input so you can match it against Jira.
The same inputs appear on the tickets page itself, under Destination — that is where you are when you find out a value is missing, and a warning there names the fields that will make the tracker reject the batch. Filling them in either place is the same setting.
If Jira demands a field the list doesn't show — “Team” is the usual one — use Add a field. Whether a field is required is only as good as what Jira advertises: a workflow validator, a plan rule or a project automation can refuse a ticket over a field the create screen calls optional. So the whole create screen is offered, with the rest of your site's fields behind it, and you pick the one the rejection named.
Per ticket, too. A team, component, sprint or cost centre often differs from ticket to ticket. Expand any draft on the tickets page and open its fields section: what you set there applies to that issue alone, and anything you leave blank inherits the workspace value (shown in the box as a placeholder).
- Values are read live from the project, so the form always matches the create screen as it is now — not as it was when you connected. Two workspaces see different fields, because they have different fields.
- They're stored as plain configuration, not as credentials: a cost centre isn't a secret, and the form has to read them back. Keep secrets out of these fields.
- Your values can never displace the parts of the ticket Harpoon owns — the summary, description, issue type and project always win.
- Reconnecting clears them, because the new credentials may point at a different project with a different create screen.
How your credentials are handled#
- Encrypted at rest. The whole provider config is sealed with AES-256-GCM before it touches the database, and unsealed in exactly one place: the moment an issue is created.
- Never returned to the browser. No API response contains credential material — the only string the connections list gives back is the identity label derived from the provider's own verification response. Nothing is logged.
- Scoped to your workspace. One workspace can never read or file with another's connection.
- Your Jira URL is checked before it's fetched. Because Harpoon calls that URL from its own servers, it goes through the same guard as every other outbound fetch: a private-network or internal address is refused outright.
- Disconnect any time. Removing a connection deletes the sealed credential; ticket generation carries on unaffected, minus the file-for-real button.
Only what's needed to create an issue is ever sent to your tracker: the ticket title, body, priority and labels. Harpoon never reads your existing issues. See Security & your data.
If something doesn't work#
| What you see | What it means |
|---|---|
| The Issue tracker card shows “Could not load your integrations.” | Your workspace isn't on Fleet, or you're signed in as a member rather than an owner/admin. Check your plan and role. |
| “Jira rejected that email and API token.” | The token is wrong, revoked, or belongs to a different account than the email. Re-create it at id.atlassian.com and reconnect. |
| “Jira project … was not found, or this account can’t see it.” | The key is mistyped, or that Atlassian account has no access to the project. Open the project in Jira as that user to confirm. |
| “That Linear team was not found for this API key.” | The API key is valid but the team id belongs to another workspace, or is mistyped. |
| The tickets page lists drafts but offers “Connect a tracker” instead of a create button. | No tracker is connected for this workspace — the payload-only path. The drafts are still yours: copy them out as Markdown, or connect a tracker in Settings. |
| A ticket row shows a red “Jira rejected this issue” message. | That is Jira’s own words for what it refused — usually a required field on your create screen. Adjust the project (or the screen) and press Retry; the other tickets are unaffected. |