Skip to main content
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.

Configuring Linear as a Source

In the 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’ll need the following credentials from your Linear account:
  • Access Token: The token is generated after you authenticate with your Linear account. You should log in with an account that has sufficient permissions to access data from your Linear workspace.
  • Start Date: The earliest record date to 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, 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. 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 page. If needed, manually trigger the source extraction by clicking on the arrow button. Once executed, your data will appear in your Catalog.
For you to be able to see it on your Catalog, you need at least one successful source run.

Streams and Fields

Below you’ll find all available data streams from Linear and their corresponding fields:
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
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
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
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
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
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
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

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.

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.

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

SQL code

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

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

SQL code

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

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

SQL code

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

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

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
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.