> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nekt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Linear as a data source

> Bring data from Linear to Nekt.

Linear is a modern project management and issue tracking tool designed for software development teams. It provides a streamlined interface for managing issues, projects, cycles, and team workflows with powerful features for planning, tracking, and collaboration.

<img height="50" src="https://mintcdn.com/nekt/3MFKt2g7jzqFpztO/assets/logo/logo-linear.jpeg?fit=max&auto=format&n=3MFKt2g7jzqFpztO&q=85&s=35ea6b4a2d3cbed5e2cbb91cbc5277ef" data-path="assets/logo/logo-linear.jpeg" />

## Configuring Linear as a Source

In the [Sources](https://app.nekt.ai/sources) tab, click on the "Add source" button located on the top right of your screen. Then, select the Linear option from the list of connectors.

Click **Next** and you'll be prompted to add your access.

### 1. Add account access

You can authenticate with Linear using OAuth credentials for a secure, refreshable access to your Linear workspace. This is the preferred authentication method as it maintains long-term access without requiring manual token renewal.

* **Start Date**: The earliest record date to sync. This determines how far back in time the connector will fetch data during the initial sync.

Once you fill the configuration, click **Next**.

### 2. Select streams

Choose which data streams you want to sync - you can select all streams or pick specific ones that matter most to you.

> Tip: The stream can be found more easily by typing its name.

Select the streams and click **Next**.

### 3. Configure data streams

Customize how you want your data to appear in your catalog. Select a name for each table (which will contain the fetched data) and the type of sync.

* **Table name**: we suggest a name, but feel free to customize it. You have the option to add a **prefix** and make this process faster!

* **Sync Type**: you can choose between INCREMENTAL and FULL\_TABLE.
  * Incremental: every time the extraction happens, we'll get only the new data - which is good if, for example, you want to keep every record ever fetched.
  * Full table: every time the extraction happens, we'll get the current state of the data - which is good if, for example, you don't want to have deleted data in your catalog.

Once you are done configuring, click **Next**.

### 4. Configure data source

Describe your data source for easy identification within your organization, not exceeding 140 characters.

To define your [Trigger](https://docs.nekt.com/runs/scheduling-and-triggers), consider how often you want data to be extracted from this source. This decision usually depends on how frequently you need the new table data updated (every day, once a week, or only at specific times).

Optionally, you can determine when to execute a [full sync](https://docs.nekt.com/get-started/core-concepts/types-of-sync#additional-full-sync). This will complement the incremental data extractions, ensuring that your data is completely synchronized with your source every once in a while.

Once you are ready, click **Next** to finalize the setup.

### 5. Check your new source

You can view your new source on the [Sources](https://app.nekt.ai/sources) page. If needed, manually trigger the source extraction by clicking on the arrow button. Once executed, your data will appear in your Catalog.

<Warning>For you to be able to see it on your [Catalog](https://app.nekt.ai/catalog), you need at least one successful source run.</Warning>

# Streams and Fields

Below you'll find all available data streams from Linear and their corresponding fields:

<AccordionGroup>
  <Accordion title="Issues">
    Stream containing all issues/tickets in your Linear workspace.

    **Key Fields:**

    * `id` - Unique identifier for the issue
    * `title` - Title of the issue
    * `description` - Detailed description of the issue
    * `priority` - Priority level (1-4, where 1 is highest)
    * `estimate` - Estimated effort/story points
    * `createdAt` - When the issue was created
    * `updatedAt` - When the issue was last updated
    * `startedAt` - When work on the issue started
    * `completedAt` - When the issue was completed
    * `dueDate` - Due date for the issue
    * `identifier` - Human-readable identifier (e.g., ENG-123)
    * `number` - Issue number
    * `priorityLabel` - Priority as text (Urgent, High, Medium, Low)
    * `url` - URL to the issue in Linear

    **Nested Objects:**

    * `state` - Issue state (id, name, type, color)
    * `team` - Assigned team (id, name, key)
    * `assignee` - Assigned user (id, name, email, displayName)
    * `creator` - User who created the issue
    * `project` - Associated project (id, name, description, state, priority)
    * `cycle` - Associated cycle (id, name, number, startsAt, endsAt)
    * `parent` - Parent issue if this is a sub-issue
    * `labels` - Array of labels attached to the issue
    * `subscribers` - Users subscribed to the issue
    * `comments` - Comments on the issue
  </Accordion>

  <Accordion title="Users">
    Stream containing all users in your Linear organization.

    **Key Fields:**

    * `id` - Unique identifier for the user
    * `name` - Full name of the user
    * `displayName` - Display name
    * `email` - Email address
    * `active` - Whether the user is active
    * `admin` - Whether the user has admin privileges
    * `guest` - Whether the user is a guest
    * `createdAt` - When the user was created
    * `updatedAt` - When the user was last updated
    * `lastSeen` - When the user was last seen
    * `avatarUrl` - URL to the user's avatar
    * `timezone` - User's timezone
    * `statusEmoji` - Current status emoji
    * `statusLabel` - Current status label
    * `createdIssueCount` - Number of issues created by this user

    **Nested Objects:**

    * `organization` - Organization the user belongs to
    * `teams` - Array of teams the user is a member of
  </Accordion>

  <Accordion title="Teams">
    Stream containing all teams in your Linear organization.

    **Key Fields:**

    * `id` - Unique identifier for the team
    * `name` - Name of the team
    * `key` - Team key/abbreviation
    * `description` - Team description
    * `private` - Whether the team is private
    * `color` - Team color
    * `icon` - Team icon
    * `createdAt` - When the team was created
    * `updatedAt` - When the team was last updated
    * `cyclesEnabled` - Whether cycles are enabled for this team
    * `cycleDuration` - Duration of cycles in weeks
    * `cycleStartDay` - Day of the week cycles start
    * `defaultIssueEstimate` - Default estimate for new issues
    * `issueEstimationType` - Type of estimation used

    **Nested Objects:**

    * `defaultIssueState` - Default state for new issues
    * `organization` - Organization the team belongs to
  </Accordion>

  <Accordion title="Projects">
    Stream containing all projects in your Linear workspace.

    **Key Fields:**

    * `id` - Unique identifier for the project
    * `name` - Name of the project
    * `description` - Project description
    * `state` - Current state of the project
    * `priority` - Project priority
    * `createdAt` - When the project was created
    * `updatedAt` - When the project was last updated
    * `startedAt` - When the project was started
    * `completedAt` - When the project was completed
    * `targetDate` - Target completion date
    * `progress` - Progress percentage (0-1)
    * `url` - URL to the project in Linear
    * `color` - Project color
    * `icon` - Project icon

    **Nested Objects:**

    * `creator` - User who created the project
    * `lead` - Project lead
    * `members` - Array of project members
    * `teams` - Array of teams associated with the project
  </Accordion>

  <Accordion title="Cycles">
    Stream containing all cycles (sprints) in your Linear workspace.

    **Key Fields:**

    * `id` - Unique identifier for the cycle
    * `name` - Name of the cycle
    * `description` - Cycle description
    * `number` - Cycle number
    * `startsAt` - When the cycle starts
    * `endsAt` - When the cycle ends
    * `createdAt` - When the cycle was created
    * `updatedAt` - When the cycle was last updated
    * `completedAt` - When the cycle was completed
    * `progress` - Progress percentage (0-1)
    * `completedIssueCountHistory` - Array of completed issue counts over time
    * `issueCountHistory` - Array of total issue counts over time
    * `scopeHistory` - Array of scope changes over time

    **Nested Objects:**

    * `team` - Team this cycle belongs to
  </Accordion>

  <Accordion title="Issue Labels">
    Stream containing all issue labels in your Linear workspace.

    **Key Fields:**

    * `id` - Unique identifier for the label
    * `name` - Name of the label
    * `description` - Label description
    * `color` - Label color
    * `createdAt` - When the label was created
    * `updatedAt` - When the label was last updated

    **Nested Objects:**

    * `creator` - User who created the label
    * `team` - Team this label belongs to
    * `parent` - Parent label if this is a child label
    * `children` - Array of child labels
  </Accordion>

  <Accordion title="Customers">
    Stream containing all customers in your Linear workspace.

    **Key Fields:**

    * `id` - Unique identifier for the customer
    * `name` - Customer name
    * `domains` - Array of customer domains
    * `externalIds` - Array of external identifiers
    * `approximateNeedCount` - Approximate number of customer needs
    * `createdAt` - When the customer was created
    * `updatedAt` - When the customer was last updated

    **Nested Objects:**

    * `status` - Customer status
    * `tier` - Customer tier
  </Accordion>
</AccordionGroup>

## Data Model

The following diagram illustrates the relationships between the core data streams in Linear. The arrows indicate the join keys that link the different entities, providing a clear overview of the data structure.

```mermaid theme={null}
graph TD;
    Issues("Issues")
    Users("Users")
    Teams("Teams")
    Projects("Projects")
    Cycles("Cycles")
    IssueLabels("Issue Labels")
    Customers("Customers")

    Issues -- "assignee.id" --> Users
    Issues -- "creator.id" --> Users
    Issues -- "team.id" --> Teams
    Issues -- "project.id" --> Projects
    Issues -- "cycle.id" --> Cycles
    Projects -- "creator.id" --> Users
    Projects -- "lead.id" --> Users
    Cycles -- "team.id" --> Teams
    IssueLabels -- "creator.id" --> Users
    IssueLabels -- "team.id" --> Teams
```

## Use Cases for Data Analysis

Here are some valuable business intelligence use cases when consolidating Linear data, along with ready-to-use SQL queries that you can run on [Explorer](https://app.nekt.ai/explorer).

### 1. Team Velocity and Sprint Performance

Analyze team productivity and sprint completion rates across cycles.

**Business Value:**

* Track team velocity over time
* Identify capacity planning opportunities
* Measure sprint completion rates
* Optimize cycle duration and scope

<Accordion title="SQL code" defaultOpen>
  ```sql theme={null}
  WITH
    cycle_metrics AS (
      SELECT
        c.id AS cycle_id,
        c.name AS cycle_name,
        c.number AS cycle_number,
        c.team.name AS team_name,
        c.startsAt AS cycle_start,
        c.endsAt AS cycle_end,
        c.progress AS cycle_progress,
        DATE_DIFF('day', c.startsAt, c.endsAt) AS cycle_duration_days,
        CASE 
          WHEN c.completedAt IS NOT NULL THEN 'Completed'
          WHEN c.endsAt < CURRENT_TIMESTAMP THEN 'Overdue'
          WHEN c.startsAt <= CURRENT_TIMESTAMP THEN 'Active'
          ELSE 'Planned'
        END AS cycle_status
      FROM nekt_raw.linear_cycles c
      WHERE c.startsAt >= CURRENT_DATE - INTERVAL '6' MONTH
    ),
    issue_metrics AS (
      SELECT
        i.cycle.id AS cycle_id,
        COUNT(*) AS total_issues,
        COUNT(CASE WHEN i.completedAt IS NOT NULL THEN 1 END) AS completed_issues,
        SUM(COALESCE(i.estimate, 0)) AS total_story_points,
        SUM(CASE WHEN i.completedAt IS NOT NULL THEN COALESCE(i.estimate, 0) ELSE 0 END) AS completed_story_points,
        AVG(DATE_DIFF('hour', i.createdAt, COALESCE(i.completedAt, CURRENT_TIMESTAMP))) AS avg_completion_time_hours
      FROM nekt_raw.linear_issues i
      WHERE i.cycle.id IS NOT NULL
      GROUP BY i.cycle.id
    )
  SELECT
    cm.team_name,
    cm.cycle_name,
    cm.cycle_number,
    cm.cycle_start,
    cm.cycle_end,
    cm.cycle_duration_days,
    cm.cycle_status,
    ROUND(cm.cycle_progress * 100, 2) AS progress_percentage,
    COALESCE(im.total_issues, 0) AS total_issues,
    COALESCE(im.completed_issues, 0) AS completed_issues,
    ROUND(
      CAST(COALESCE(im.completed_issues, 0) AS DOUBLE) / NULLIF(im.total_issues, 0) * 100,
      2
    ) AS completion_rate_percentage,
    COALESCE(im.total_story_points, 0) AS total_story_points,
    COALESCE(im.completed_story_points, 0) AS completed_story_points,
    ROUND(im.avg_completion_time_hours / 24.0, 2) AS avg_completion_time_days
  FROM cycle_metrics cm
  LEFT JOIN issue_metrics im ON cm.cycle_id = im.cycle_id
  ORDER BY cm.team_name, cm.cycle_start DESC
  ```
</Accordion>

### 2. Issue Resolution Analysis

Track issue resolution patterns and identify bottlenecks in the development process.

**Business Value:**

* Identify issues that take longest to resolve
* Analyze resolution patterns by priority and team
* Track issue aging and identify blockers
* Optimize workflow efficiency

<Accordion title="SQL code" defaultOpen>
  ```sql theme={null}
  WITH
    issue_analysis AS (
      SELECT
        i.id,
        i.identifier,
        i.title,
        i.priority,
        i.priorityLabel,
        i.team.name AS team_name,
        i.assignee.displayName AS assignee_name,
        i.state.name AS current_state,
        i.state.type AS state_type,
        i.createdAt,
        i.startedAt,
        i.completedAt,
        i.estimate,
        DATE_DIFF('day', i.createdAt, COALESCE(i.completedAt, CURRENT_TIMESTAMP)) AS age_days,
        CASE
          WHEN i.completedAt IS NOT NULL THEN 
            DATE_DIFF('day', i.createdAt, i.completedAt)
        END AS resolution_time_days,
        CASE
          WHEN i.completedAt IS NOT NULL THEN 'Completed'
          WHEN i.startedAt IS NOT NULL THEN 'In Progress'
          ELSE 'Not Started'
        END AS issue_status,
        CASE
          WHEN i.priority = 1 THEN 'Urgent'
          WHEN i.priority = 2 THEN 'High'
          WHEN i.priority = 3 THEN 'Medium'
          WHEN i.priority = 4 THEN 'Low'
          ELSE 'No Priority'
        END AS priority_category
      FROM nekt_raw.linear_issues i
      WHERE i.createdAt >= CURRENT_DATE - INTERVAL '3' MONTH
    ),
    team_summary AS (
      SELECT
        team_name,
        priority_category,
        COUNT(*) AS total_issues,
        COUNT(CASE WHEN issue_status = 'Completed' THEN 1 END) AS completed_issues,
        AVG(resolution_time_days) AS avg_resolution_days,
        PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_days) AS median_resolution_days,
        COUNT(CASE WHEN age_days > 30 AND issue_status != 'Completed' THEN 1 END) AS stale_issues
      FROM issue_analysis
      GROUP BY team_name, priority_category
    )
  SELECT
    team_name,
    priority_category,
    total_issues,
    completed_issues,
    ROUND(
      CAST(completed_issues AS DOUBLE) / NULLIF(total_issues, 0) * 100,
      2
    ) AS completion_rate_percentage,
    ROUND(avg_resolution_days, 2) AS avg_resolution_days,
    ROUND(median_resolution_days, 2) AS median_resolution_days,
    stale_issues,
    ROUND(
      CAST(stale_issues AS DOUBLE) / NULLIF(total_issues, 0) * 100,
      2
    ) AS stale_issue_percentage
  FROM team_summary
  WHERE total_issues > 0
  ORDER BY team_name, 
    CASE priority_category 
      WHEN 'Urgent' THEN 1 
      WHEN 'High' THEN 2 
      WHEN 'Medium' THEN 3 
      WHEN 'Low' THEN 4 
      ELSE 5 
    END
  ```
</Accordion>

### 3. Project Progress and Resource Allocation

Monitor project health and team allocation across active projects.

**Business Value:**

* Track project progress and identify at-risk projects
* Analyze resource allocation across projects
* Monitor project timeline adherence
* Optimize team capacity planning

<Accordion title="SQL code" defaultOpen>
  ```sql theme={null}
  WITH
    project_metrics AS (
      SELECT
        p.id AS project_id,
        p.name AS project_name,
        p.state AS project_state,
        p.priority AS project_priority,
        p.progress AS project_progress,
        p.createdAt AS project_created,
        p.startedAt AS project_started,
        p.completedAt AS project_completed,
        p.targetDate AS target_date,
        p.lead.displayName AS project_lead,
        CASE
          WHEN p.completedAt IS NOT NULL THEN 'Completed'
          WHEN p.targetDate < CURRENT_DATE AND p.completedAt IS NULL THEN 'Overdue'
          WHEN p.startedAt IS NOT NULL THEN 'In Progress'
          ELSE 'Not Started'
        END AS project_status,
        CASE
          WHEN p.targetDate IS NOT NULL THEN
            DATE_DIFF('day', CURRENT_DATE, p.targetDate)
        END AS days_to_target
      FROM nekt_raw.linear_projects p
      WHERE p.archivedAt IS NULL
    ),
    project_issues AS (
      SELECT
        i.project.id AS project_id,
        COUNT(*) AS total_issues,
        COUNT(CASE WHEN i.completedAt IS NOT NULL THEN 1 END) AS completed_issues,
        COUNT(CASE WHEN i.startedAt IS NOT NULL AND i.completedAt IS NULL THEN 1 END) AS in_progress_issues,
        COUNT(CASE WHEN i.startedAt IS NULL THEN 1 END) AS not_started_issues,
        SUM(COALESCE(i.estimate, 0)) AS total_story_points,
        SUM(CASE WHEN i.completedAt IS NOT NULL THEN COALESCE(i.estimate, 0) ELSE 0 END) AS completed_story_points,
        COUNT(DISTINCT i.assignee.id) AS unique_assignees,
        COUNT(DISTINCT i.team.id) AS teams_involved
      FROM nekt_raw.linear_issues i
      WHERE i.project.id IS NOT NULL
      GROUP BY i.project.id
    )
  SELECT
    pm.project_name,
    pm.project_state,
    pm.project_status,
    pm.project_lead,
    pm.project_created,
    pm.project_started,
    pm.target_date,
    pm.days_to_target,
    ROUND(pm.project_progress * 100, 2) AS progress_percentage,
    COALESCE(pi.total_issues, 0) AS total_issues,
    COALESCE(pi.completed_issues, 0) AS completed_issues,
    COALESCE(pi.in_progress_issues, 0) AS in_progress_issues,
    COALESCE(pi.not_started_issues, 0) AS not_started_issues,
    ROUND(
      CAST(COALESCE(pi.completed_issues, 0) AS DOUBLE) / NULLIF(pi.total_issues, 0) * 100,
      2
    ) AS issue_completion_rate,
    COALESCE(pi.total_story_points, 0) AS total_story_points,
    COALESCE(pi.completed_story_points, 0) AS completed_story_points,
    ROUND(
      CAST(COALESCE(pi.completed_story_points, 0) AS DOUBLE) / NULLIF(pi.total_story_points, 0) * 100,
      2
    ) AS story_points_completion_rate,
    COALESCE(pi.unique_assignees, 0) AS team_members_involved,
    COALESCE(pi.teams_involved, 0) AS teams_involved,
    CASE
      WHEN pm.project_status = 'Overdue' THEN 'At Risk'
      WHEN pm.days_to_target <= 7 AND pm.project_progress < 0.8 THEN 'At Risk'
      WHEN pm.project_progress >= 0.8 THEN 'On Track'
      ELSE 'Monitor'
    END AS health_status
  FROM project_metrics pm
  LEFT JOIN project_issues pi ON pm.project_id = pi.project_id
  ORDER BY 
    CASE pm.project_status 
      WHEN 'Overdue' THEN 1 
      WHEN 'In Progress' THEN 2 
      ELSE 3 
    END,
    pm.days_to_target ASC NULLS LAST
  ```
</Accordion>

## Implementation Notes

### Data Quality Considerations

* Monitor issue state transitions for workflow analysis
* Validate estimate accuracy against actual completion times
* Track label usage consistency across teams
* Ensure proper project and cycle assignment

### Authentication & Security

* OAuth authentication is recommended over legacy access tokens for better security
* Refresh tokens are automatically managed to maintain long-term access
* Use service accounts with appropriate permissions for production environments

### Automation Opportunities

* Schedule weekly team velocity reports
* Set up alerts for overdue issues and projects
* Automate sprint retrospective data collection
* Generate monthly team performance dashboards

### Best Practices

* Use incremental sync for large datasets to improve performance
* Set appropriate start\_date to avoid syncing unnecessary historical data
* Migrate from legacy access tokens to OAuth authentication when possible

These SQL transformations provide a foundation for comprehensive Linear analytics. Customize the date ranges, filters, and metrics based on your specific team workflows and reporting needs.

## Skills for agents

<Card title="Download Linear skills file" icon="wand-magic-sparkles" href="/sources/linear.md">
  Linear connector documentation as plain markdown, for use in AI agent contexts.
</Card>
