# Jira and Linear

> Semaloop has no native Jira or Linear integration. File failed test runs as tickets with a webhook or the MCP server.

Rendered version: https://docs.semaloop.com/integrations/jira-and-linear/

Semaloop has no native Jira or Linear integration, but you can file failed test runs as tickets in either one with a webhook or with the MCP server. This page covers both routes.

|                          | Webhook                                | MCP server                                               |
| ------------------------ | -------------------------------------- | -------------------------------------------------------- |
| When a ticket is created | Automatically, as each run fails       | When you ask your coding agent                           |
| What you run             | A small handler, or an automation tool | A coding agent connected to two MCP servers              |
| Best for                 | Every failure, with no one in the loop | Recurring issues, combined with context you already have |

## Route 1: a webhook that creates a ticket

Semaloop sends a signed `test_run.status_changed.v1` event when a run reaches `success`, `failure` or `error`. See [webhooks](/integrations/webhooks/) for the payload.

1. Deploy an endpoint that can receive a `POST` request. It can be a small serverless function or a step in an automation tool.
2. Go to **Settings** → **Webhooks** → **Set up webhooks** and add the endpoint.
3. Add a transformation to the endpoint so only failures are delivered. The [webhooks page](/integrations/webhooks/) has the code.
4. In the endpoint, verify the signature, then create the ticket from `data.failureReason` and `data.url`.
5. Deduplicate on the event’s `id`. It is stable per run and status, and a run that is retried automatically only sends an event for its final attempt.

This handler verifies the signature with the [Svix](https://docs.svix.com/receiving/verifying-payloads/how) library and creates a Jira issue. Change the site, project key and issue type to match your account.

```js
import { Webhook } from "svix";


export default {
  async fetch(request, env) {
    const body = await request.text();
    const event = new Webhook(env.SEMALOOP_WEBHOOK_SECRET).verify(body, {
      "svix-id": request.headers.get("svix-id"),
      "svix-timestamp": request.headers.get("svix-timestamp"),
      "svix-signature": request.headers.get("svix-signature"),
    });


    const { failureReason, url } = event.data;
    await fetch(`https://${env.JIRA_SITE}.atlassian.net/rest/api/2/issue`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Basic ${btoa(`${env.JIRA_EMAIL}:${env.JIRA_API_TOKEN}`)}`,
      },
      body: JSON.stringify({
        fields: {
          project: { key: "APP" },
          issuetype: { name: "Bug" },
          summary: `Semaloop test failed: ${failureReason}`,
          description: `${failureReason}\n\nRun: ${url}`,
        },
      }),
    });
    return new Response("ok");
  },
};
```

For Linear, replace the `fetch` call with a GraphQL mutation against `https://api.linear.app/graphql`, sending your Linear API key in the `Authorization` header:

```js
await fetch("https://api.linear.app/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: env.LINEAR_API_KEY,
  },
  body: JSON.stringify({
    query: `mutation ($input: IssueCreateInput!) {
      issueCreate(input: $input) { success issue { identifier url } }
    }`,
    variables: {
      input: {
        teamId: env.LINEAR_TEAM_ID,
        title: `Semaloop test failed: ${failureReason}`,
        description: `${failureReason}\n\nRun: ${url}`,
      },
    },
  }),
});
```

These snippets show the shape of each call. Check your tracker’s API documentation for the authentication, field names and required fields your account uses.

## Route 2: a coding agent connected to both servers

Connect your coding agent to the [Semaloop MCP server](/integrations/mcp-server/) and to your tracker’s own MCP server, then ask it to do the filing. It can read your apps’ runs and recurring [issues](/issues-and-signals/understanding-issues/) from Semaloop and create tickets in Jira or Linear, combined with whatever else you have to hand, such as support tickets or meeting notes. For example:

> Use Semaloop to list the failed runs on the latest build of our iOS app and the open issues. For each one that isn’t already in Linear, create an issue with the failure reason, a link to the run, and what the video and logs show.

Reading runs and issues over MCP is open to every member of your organisation. The agent only needs write access in your tracker.

## Related

- Index of every Semaloop docs page: https://docs.semaloop.com/llms.txt
- All Semaloop docs in one file: https://docs.semaloop.com/llms-full.txt
