LogoLogo
SlackLogin
  • Overview
    • Welcome
  • Setup & Configuration
    • Connecting to Trunk
    • Managing Your Organization
      • GitHub App Permissions
  • Integration with Slack
  • Flaky Tests (Beta)
    • Overview
    • Get Started
      • Test Frameworks
        • Android
        • Bazel
        • Behave
        • cargo-nextest
        • Cypress
        • Dart Test
        • Go
        • GoogleTest
        • Gradle
        • Jasmine
        • Jest
        • Karma
        • Maven
        • minitest
        • Mocha
        • Nightwatch
        • NUnit
        • Pest
        • PHPUnit
        • Playwright
        • Pytest
        • Robot Framework
        • RSpec
        • Swift Testing
        • Vitest
        • XCTest
        • Other Test Frameworks
      • CI Providers
        • Azure DevOps Pipelines
        • BitBucket Pipelines
        • Buildkite
        • CircleCI
        • Drone CI
        • GitHub Actions
        • GitLab
        • Jenkins
        • Semaphore CI
        • TeamCity
        • Travis CI
        • Other CI Providers
    • Dashboard
    • Flaky Test Detection
    • Quarantining
    • PR Comments
    • Ticketing Integrations
      • Jira Integration
      • Linear Integration
      • Other Ticketing Platforms
    • Webhooks
      • Slack Integration
      • Microsoft Teams Integration
      • GitHub Issues Integration
      • Linear Integration
    • Uploader CLI Reference
  • Code Quality
    • Overview
    • Why Metalinters?
      • How does it work?
      • Why Code Quality?
    • Setup & Installation
      • Initialize Trunk
      • Local Linting
      • Linting in CI
      • Nightly Report (Deprecated)
    • IDE Integration
      • VSCode
      • Neovim
      • GitHub Codespaces
    • Linters
      • Supported Linters
        • Actionlint
        • Ansible-lint
        • Autopep8
        • Bandit
        • Biome
        • Black
        • Brakeman
        • buf
        • Buildifier
        • cfnlint
        • Checkov
        • circleci
        • ClangFormat
        • clang-tidy
        • Clippy
        • cmake-format
        • codespell
        • cspell
        • cue-fmt
        • dart
        • deno
        • Detekt
        • djlint
        • dotenv-linter
        • dotnet-format
        • dustilock
        • ESLint
        • Flake8
        • git-diff-check
        • Gitleaks
        • Gofmt
        • gofumpt
        • goimports
        • gokart
        • golangci-lint
        • golines
        • google-java-format
        • graphql-schema-linter
        • hadolint
        • haml-lint
        • isort
        • iwyu
        • ktlint
        • kube-linter
        • markdown-link-check
        • markdown-table-prettify
        • Markdownlint
        • markdownlint-cli2
        • mypy
        • nancy
        • nixpkgs-fmt
        • opa
        • OSV-Scanner
        • Oxipng
        • perlcritic
        • perltidy
        • php-cs-fixer
        • phpstan
        • pmd
        • pragma-once
        • pre-commit-hooks
        • Prettier
        • prisma
        • psscriptanalyzer
        • Pylint
        • pyright
        • regal
        • remark-lint
        • renovate
        • rome
        • rubocop
        • Ruff
        • rufo
        • rustfmt
        • scalafmt
        • semgrep
        • ShellCheck
        • shfmt
        • sort-package-json
        • sourcery
        • sql-formatter
        • SQLFluff
        • sqlfmt
        • squawk
        • standardrb
        • stringslint
        • stylelint
        • stylua
        • SVGO
        • swiftformat
        • swiftlint
        • taplo
        • Terraform
        • terragrunt
        • terrascan
        • TFLint
        • tfsec
        • tofu
        • Trivy
        • Trufflehog
        • txtpbfmt
        • vale
        • Yamllint
        • yapf
      • Run Linters
      • Manage Linters
      • Configure Linters
      • Ignoring Issues and Files
      • Custom Linters
      • Shared Configs
      • Upgrades
    • CI Setup
      • GitHub Integration
      • Manual Setup
    • Debugging
    • Licensing
  • Merge Queue
    • Overview
    • How does it work?
    • Setup
      • Quick Start
      • Settings
      • Integration for Slack
    • Concepts and Optimizations
      • Predictive Testing
      • Optimistic Merging
      • Pending Failure Depth
      • Anti-Flake Protection
      • Batching
      • Parallel Queues
        • Bazel
        • Nx
        • API
      • FAQ
    • Priority
    • Managing Merge Queue
      • Using the Merge UI
      • Metrics
      • Command Line
    • Webhooks
    • Reference
  • CLI & API References
    • CLI Reference
      • Install
      • Getting Started
        • Code Quality
        • Merge Queue
        • Flaky Tests
        • Tools
        • Actions
          • Git Hooks
        • Announce
      • Compatibility
      • Caching
      • Commands Reference
        • Code Quality
        • Actions
        • Merge
      • Configuration
        • Plugins
          • Share Config Between Codebases
          • Exporting Linter Configs
        • Runtimes
        • Tools
        • Lint
          • Definitions
          • Commands
          • Output
          • Output Parsing
          • Files and Caching
          • Dependencies
          • Auto-Enable
        • Actions
          • Notifications
          • Logging and Troubleshooting
        • Merge
        • Telemetry
        • Per User Overrides
    • API Reference
      • Flaky Tests
      • Merge Queue
      • Webhooks Reference
  • Pricing & Security
    • Security
  • Billing
  • Community & Support
  • Links
    • Open App
    • Slack Community
    • Changelog
    • Feature Requests
On this page
  • 1. Create a Linear Personal Access Token
  • 2. Add a New Webhook In Trunk
  • 3. Add Custom Headers
  • 4. Find Your Linear Team, Project, and Label IDs
  • 5. Customize Your Transformation
  • 6. Test Your Webhook
  • 7. Monitoring Webhooks
  • Congratulations!
Edit on GitHub
  1. Flaky Tests (Beta)
  2. Webhooks

Linear Integration

Learn how to automatically create Linear issues with Flaky Test webhooks

Last updated 18 days ago

Trunk allows you to automate Linear Issue creation through webhooks. This will allow you to create Linear issues and auto-assign according to .

This guide will walk you through integrating Trunk Flaky Tests with Linear Issues through webhooks. You will be able to automatically generate Linear issues for new flaky tests found in your repo that impact more than 2 PRs. This guide should take 15 minutes to complete.

Trunk also has a for ticket creation. You only need to use webhooks if you want to automate ticket creation or need additional customization.

1. Create a Linear Personal Access Token

Before you can create a webhook to automate GitHub Issue creation, you need to create an API token to authorize your requests.

  1. In the Linear app, navigate to settings by holding G and pressing S, or by clicking on your profile on the top left and clicking Settings.

  2. Under Account > Security & Access > Personal API Keys, Click New API Key to create a new access token.

  3. Copy the new API key and save it in a secure location. You'll need to use this later.

2. Add a New Webhook In Trunk

Trunk uses Svix to integrate with other services, such as creating Linear Issues through webhooks.

You can create a new endpoint by:

  1. From your profile on the top right, navigate to Settings

  2. Under Organization > Webhooks, click Automate Linear Issues Creation.

  3. Paste the Linear GraphQL API endpoint into Endpoint URL, which is: https://api.linear.app/graphql.

  4. Create the new endpoint. You will be redirected to the endpoint configuration view.

3. Add Custom Headers

The Linear GraphQL API requires some custom headers. You can configure custom headers in the endpoint configuration:

  1. You can add custom headers under Webhooks > Advanced > Custom Headers.

  2. Fill in the Key and Value referencing the table below, and click the + button to add each header.

You'll need to configure the following headers.

Key
Value

Authorization

<YOUR_API_TOKEN>

4. Find Your Linear Team, Project, and Label IDs

You need to find your Linear team, project, and label IDs to create issues with the appropriate labeling. You can do this by querying your Linear project using cURL.

Team ID

First, you'll need to find your team ID so you can create Linear issues under the correct team. You can make a request in your terminal using cURL, or a similar tool.

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: <LINEAR_API_KEY>" \
  --data '{
    "query": "query Teams { teams { nodes { id name } }}"
  }' \
  https://api.linear.app/graphql

You will receive a response that contains your team UID, for example:

{
  "data": {
    "teams": {
      "nodes": [
        {
          "id": "9bd0672b-7766-4a7c-3233-8ce37fdbb790",
          "name": "Your Linear Team"
        }
      ]
    }
  }
}

Project ID

If you want to create issues under a specific project, you'll need to find its project ID. You can use a query like this:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: lin_api_vw3gMdb2NJN9TQ66JCgBKLqNSNY6I8cH5qxwM6EW" \
  --data '{
    "query": "query Projects { projects { nodes { id name } }}"
  }' \
  https://api.linear.app/graphql

You'll receive a response that contains your projects and their IDs, for example:

{
  "data": {
    "projects": {
      "nodes": [
        {
          "id": "ef19b35e-ce4f-4132-9705-811d4d6c8c08",
          "name": "Flaky Tests"
        }
      ]
    }
  }
}

Label ID

If you want to create issues with a specific label, you'll need to find its label ID. You can use a query like this:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: lin_api_vw3gMdb2NJN9TQ66JCgBKLqNSNY6I8cH5qxwM6EW" \
  --data '{
    "query": "query OrgLabels { organization { labels { nodes { id name } } }}"
  }' \
  https://api.linear.app/graphql

You'll receive a response that contains your labels and their IDs, for example:

{
  "data": {
    "organization": {
      "labels": {
        "nodes": [
          {
            "id": "e0e9f98e-c90c-40cd-939e-06ff7bd57b45",
            "name": "Feature"
          },
          {
            "id": "ce07d3bd-dee8-4bf6-979e-778dd94f15af",
            "name": "Bug"
          },
          {
            "id": "536dd774-dc33-4e70-aecc-8b00d1f04a9d",
            "name": "Improvement"
          },
          ...
        ]
      }
    }
  }
}

5. Customize Your Transformation

Transformations are custom code snippets you can write to customize the Linear issues created by the webhook. A working template transformation will be added automatically for your webhook, but you can further customize the behavior of this webhook.

  1. In the endpoint configuration view, navigate to the Advanced tab. Under Transformation, toggle the Enabled switch.

  2. Click Edit transformation to update your transformation code, and click Save to update the transformation.

The generated webhook template contains several configurable constants out of the box:

Constant
Description

LINEAR_TEAM_ID

LINEAR_PROJECT_ID

LINEAR_LABEL_IDS

PRS_IMPACTED_THRESHOLD

Issues will be created only for flaky tests that have impacted more PRs than the PRS_IMPACTED_THRESHOLD. You can adjust this value if you see many issues about low-impact flaky tests.

/**
 * @param webhook the webhook object
 * @param webhook.method destination method. Allowed values: "POST", "PUT"
 * @param webhook.url current destination address
 * @param webhook.eventType current webhook Event Type
 * @param webhook.payload JSON payload
 * @param webhook.cancel whether to cancel dispatch of the given webhook
 */

// Your Linear Team ID from step 3 above. This is required!
const LINEAR_TEAM_ID = "";
// The Linear project ID you want issues assigned to from step 3 above. Optional.
const LINEAR_PROJECT_ID = "";
// IDs of any labels you want added to the linear issue. Optional.
const LINEAR_LABEL_IDS = [];

// Below are various configs to fine-tune when an issue is created.

// At least many PRs need to be impacted for an issue to be created.
const PRS_IMPACTED_THRESHOLD = 2;

function handler(webhook) {
  const impacted_prs = webhook.payload.test_case.pull_requests_impacted_last_7d;
  const newStatus = webhook.payload.status_change.current_status.value;

  const resolvedProjectId = LINEAR_PROJECT_ID ? `"${LINEAR_PROJECT_ID}"` : undefined;
  const resolvedLinearLabels = LINEAR_LABEL_IDS.map((id) => `"${id}"`).join(",");

  // Filter for only flaky tests that impact more than the provided threshold
  if (newStatus !== "flaky" || impacted_prs < PRS_IMPACTED_THRESHOLD) {
    webhook.payload = "canceled";
    webhook.cancel = true;
    return webhook;
  }

  const description = summarizeTestCase(webhook.payload);

  // modify the webhook object...
  webhook.payload = {query: `mutation IssueCreate {
    issueCreate(
      input: {
        title: "Flaky Test: ${webhook.payload.test_case.name}"
        description: """${description}"""
        teamId: "${LINEAR_TEAM_ID}"
        projectId: ${resolvedProjectId}
        labelIds: [${resolvedLinearLabels}]
      }
    ) {
      success
      issue {
        id
        title
      }
    }
  } ` };
  return webhook;
}

function summarizeTestCase(payload) {
    const {
        status_change: {
            previous_status
        },
        test_case: {
            name,
            file_path,
            status,
            quarantine,
            repository,
            codeowners,
            failure_rate_last_7d,
            most_common_failures,
            pull_requests_impacted_last_7d,
            ticket,
            html_url
        }
    } = payload;
    // Construct a comprehensive issue body with key details
    const issueBody = `See all details on the [Trunk Test Detail page](${html_url})
 
Transition time: ${status.timestamp}
 
Latest failure: Dec 9, 2024
 
Severity (last 7 days): ${(failure_rate_last_7d * 100).toFixed(2)}% failure rate; impacting ${pull_requests_impacted_last_7d} PRs
 
Ownership: this test is owned by ${(codeowners || ['@unassigned']).join(', ')}

___
__The  most common failure reason (out of ${most_common_failures.length} identified failure reason) are:__

${
  most_common_failures.map((failure, index) => {
    return `**Reason #${index + 1}**: "${failure.summary}" \n`
  })
}

View the full stack trace on the [Test Detail page](${html_url})
    `
    return issueBody
}

(Optional) Automatic Issue Assignment

If you have CODEOWNERS configured in your repo, it will be reported by Trunk in the webhook payload. You can use this to map different CODEOWNERS to Linear assignees. You can access CODEOWNERS in the payload like this: webhook.payload.test_case.codeowners.

Since the way your owners map to your Linear user is unique to your team, you'll need to provide your own mapping to convert code owners to their Linear ID.

You can modify your issue create payload like this to include an assignee:

webhook.payload = {query: `mutation IssueCreate {
    issueCreate(
      input: {
        title: "Flaky Test: ${webhook.payload.test_case.name}"
        description: """${description}"""
        teamId: "<YOUR_TEAM_ID>"
        projectId: "<YOUR_PROJECT_ID>"
        labelIds: ["<YOUR_LABEL_ID>"]
        // Add you assignee here:
        assigneeId: "<ASSIGNEE_ID>"
      }
    ) {
      success
      issue {
        id
        title
      }
    }
  } ` };

6. Test Your Webhook

You can create test issues by delivering a mock webhook. You can do this by:

  1. In the endpoint configuration view, navigate to the Testing tab and select a Send event

  2. Under Subscribed events, select test_case.status_changedas the event type to send

  3. Click Send Example to test your webhook

7. Monitoring Webhooks

You can monitor the events and the webhook's delivery logs in the Overview tab of an endpoint configuration view.

You can see an overview of how many webhook deliveries have been attempted, how many are successful, how many are in flight, and how many fail in the Attempt Delivery Status modal.

Congratulations!

A Linear Issue will now be created when a test's health status changes to flaky and impacts more than 2 PRs. You can further modify your transformation script to customize your issues.

Login to

Review the transformation code automatically generated for Linear issues, you can customize this transformation at any time. Learn more about .

If you're having trouble adding a new webhook endpoint with Svix, please see the .

You'll need your Linear API key from .

You can test the transformation by selecting the test_case.status_changed payload and clicking Run Test. This will test the transformation but not send a message. You will learn to send a test message.

(Required) Your Linear team ID. .

(Optional) The Linear project ID assigned to new issues. .

(Optional) Array of label IDs assigned to new issues. .

Here is the provided transformation for context. You can customize your Linear Issues integration by following the and documentation.

You can see a list of past delivery attempts in the Message Attempts modal. You can filter this list by Succeeded and Failed status, and you can click on each message to see the Message content, response code, and error message of each attempt. You can learn more about and in the Svix docs.

Trunk Flaky Tests
Adding Endpoint docs from Svix
Linear API
Svix transformations
replaying messages
filtering logs
See the Trunk webhook event catalog
Learn more about consuming webhooks in the Svix docs
Learn more about Linear's API
customizing transformations
step 1
in step 6
Learn about finding your team ID
Learn more about finding your project ID
Learn about finding your l
abel IDs
CODEOWNERS
built-in Linear integration