Linear Integration
Synchronize your tasks and issues between our platform and Linear to create a unified project management workflow. This integration ensures your development team stays aligned with project goals while maintaining their preferred Linear workspace.
Overview
Why Integrate Linear?
The Linear integration provides:
- Unified Task Management: Keep tasks synchronized across both platforms
- Developer-Friendly Workflow: Maintain Linear's streamlined development experience
- Bi-directional Sync: Changes in either platform reflect automatically
- Team Alignment: Ensure project briefs align with development tickets
- Progress Tracking: Monitor development progress from project briefs
Key Features
Task Synchronization
- Bi-directional task and issue sync
- Status mapping between platforms
- Priority and label synchronization
- Assignment and ownership tracking
- Comment and activity sync
Project Management
- Linear team mapping to platform projects
- Milestone and cycle integration
- Epic and initiative alignment
- Sprint planning coordination
- Release tracking and reporting
Workflow Integration
- Automated task creation from briefs
- Custom field mapping and sync
- Workflow state synchronization
- Time tracking integration
- Progress reporting and analytics
Setup Process
Prerequisites
Before setting up the Linear integration, ensure you have:
- Linear Account: Access to a Linear workspace
- Linear Admin Access: Admin permissions in your Linear workspace
- Platform Team Admin: Admin or owner role in your platform team
- API Access: Linear API access enabled for your workspace
Step-by-Step Setup
1. Generate Linear API Key
First, create an API key in your Linear workspace:
- Access Linear Settings: Go to Linear → Settings → API
- Create Personal API Key: Click "Create API Key"
- Set Permissions: Grant appropriate permissions for the integration
- Copy API Key: Save the generated key securely
2. Configure Platform Integration
Set up the integration in your platform team settings:
- Navigate to Integrations: Team Settings → Integrations
- Select Linear: Find Linear in the available integrations
- Enter API Key: Provide your Linear API key
- Test Connection: Verify the connection is working
3. Map Teams and Projects
Configure the mapping between Linear teams and platform projects:
{
"team_mappings": [
{
"platform_project_id": "proj_123",
"linear_team_id": "team_456",
"sync_settings": {
"bi_directional": true,
"auto_create_issues": true,
"sync_comments": true,
"sync_attachments": false
}
}
]
}
4. Configure Field Mapping
Set up how fields should map between the platforms:
{
"field_mappings": {
"status": {
"pending": "Todo",
"in-progress": "In Progress",
"review": "In Review",
"completed": "Done",
"cancelled": "Canceled"
},
"priority": {
"low": 4,
"medium": 3,
"high": 2,
"critical": 1
},
"labels": {
"bug": "Bug",
"feature": "Feature",
"improvement": "Improvement"
}
}
}
Configuration Options
Synchronization Settings
Sync Direction
Bi-directional Sync (Recommended)
- Changes in either platform sync to the other
- Conflict resolution with last-modified-wins
- Complete alignment between platforms
- Requires careful workflow coordination
Platform to Linear
- Tasks created in platform become Linear issues
- Updates flow from platform to Linear only
- Maintains platform as source of truth
- Simpler conflict resolution
Linear to Platform
- Linear issues become platform tasks
- Development-driven project management
- Linear maintains issue ownership
- Good for development-focused teams
Real-time vs Batch Sync
Real-time Synchronization
const syncConfig = {
mode: 'real-time',
webhook_endpoints: {
linear: 'https://your-platform.com/webhooks/linear',
platform: 'https://linear-webhook.your-domain.com/platform'
},
retry_policy: {
max_attempts: 3,
backoff_strategy: 'exponential'
}
};
Batch Synchronization
const batchConfig = {
mode: 'batch',
schedule: 'every 15 minutes',
batch_size: 100,
processing_window: '5 minutes'
};
Advanced Configuration
Custom Field Mapping
Map custom fields between platforms:
{
"custom_fields": {
"platform_to_linear": {
"estimated_hours": {
"linear_field": "estimate",
"transform": "hours_to_points"
},
"epic_id": {
"linear_field": "parent_id",
"type": "relation"
}
},
"linear_to_platform": {
"estimate": {
"platform_field": "estimated_hours",
"transform": "points_to_hours"
},
"cycle": {
"platform_field": "sprint",
"type": "reference"
}
}
}
}
Workflow Automation
Set up automated workflows based on Linear events:
# automation-rules.yml
workflows:
- name: "Auto-create tasks from Linear issues"
trigger:
platform: "linear"
event: "issue.created"
conditions:
- field: "team_id"
operator: "equals"
value: "team_456"
actions:
- create_platform_task:
project_id: "proj_123"
title: "{{ issue.title }}"
description: "{{ issue.description }}"
assignee: "{{ issue.assignee.email }}"
- name: "Update Linear status on platform completion"
trigger:
platform: "platform"
event: "task.completed"
actions:
- update_linear_issue:
status: "Done"
comment: "Completed in platform: {{ task.completion_notes }}"
Using the Integration
Task Creation and Sync
Creating Tasks from Platform
When you create a task in your platform project:
- Automatic Linear Issue: A corresponding issue is created in Linear
- Field Mapping: Task properties are mapped to Linear issue fields
- Assignment Sync: Task assignee becomes Linear issue assignee
- Linking: Both items are linked for bi-directional updates
Platform Task Example:
{
"id": "task_789",
"title": "Implement user dashboard analytics",
"description": "Create analytics dashboard showing user engagement metrics",
"priority": "high",
"status": "pending",
"assignee": "[email protected]",
"labels": ["feature", "analytics"],
"estimated_hours": 16
}
Generated Linear Issue:
{
"id": "ISS-123",
"title": "Implement user dashboard analytics",
"description": "Create analytics dashboard showing user engagement metrics\n\n---\nCreated from Platform Task: task_789",
"priority": 2,
"state": "Todo",
"assignee": {
"email": "[email protected]"
},
"labels": ["feature", "analytics"],
"estimate": 8
}
Creating Issues from Linear
When developers create issues directly in Linear:
- Platform Task Creation: Corresponding task appears in platform project
- Context Addition: Issue context is added to relevant project brief
- Team Notification: Project stakeholders are notified of new development work
- Progress Tracking: Issue progress reflects in project dashboards
Status and Progress Tracking
Status Synchronization
Status changes automatically sync between platforms:
Linear State Changes:
- Todo → Pending (Platform)
- In Progress → In Progress (Platform)
- In Review → Review (Platform)
- Done → Completed (Platform)
- Canceled → Cancelled (Platform)
Progress Reporting
View combined progress across both platforms:
// Example progress dashboard data
const projectProgress = {
platform_tasks: {
total: 25,
completed: 18,
in_progress: 5,
pending: 2
},
linear_issues: {
total: 32,
done: 20,
in_progress: 8,
todo: 4
},
sync_status: {
synced_items: 23,
sync_errors: 2,
last_sync: "2024-01-22T14:30:00Z"
}
};
Comment and Activity Sync
Bi-directional Comments
Comments automatically sync between platforms:
Platform Comment:
Just reviewed the mockups - the analytics layout looks great!
One suggestion: can we add a date range picker for the metrics?
@sarah.developer what do you think about the implementation complexity?
Synced to Linear:
💬 **Comment from Platform** (by @john.designer)
Just reviewed the mockups - the analytics layout looks great!
One suggestion: can we add a date range picker for the metrics?
@sarah.developer what do you think about the implementation complexity?
---
*View in Platform: [task_789](https://platform.com/tasks/task_789)*
Activity Timeline
Unified activity timeline showing actions from both platforms:
- Platform: Task created, status changed, comment added
- Linear: Issue assigned, moved to In Progress, branch created
- Platform: File attached, review requested
- Linear: Pull request opened, code reviewed
- Platform: Task marked complete
Advanced Features
Linear Webhooks Integration
Set up Linear webhooks for real-time synchronization:
Webhook Configuration
// Linear webhook handler
app.post('/webhooks/linear', async (req, res) => {
const { action, data, type } = req.body;
try {
switch (type) {
case 'Issue':
await handleIssueUpdate(action, data);
break;
case 'Comment':
await handleCommentUpdate(action, data);
break;
case 'IssueLabel':
await handleLabelUpdate(action, data);
break;
}
res.status(200).send('OK');
} catch (error) {
console.error('Linear webhook error:', error);
res.status(500).send('Error processing webhook');
}
});
async function handleIssueUpdate(action, issue) {
const platformTask = await findLinkedTask(issue.id);
if (!platformTask) return;
switch (action) {
case 'update':
await syncIssueToTask(issue, platformTask);
break;
case 'delete':
await handleIssueDeleted(issue, platformTask);
break;
}
}
Custom Integrations
Linear GraphQL API
Use Linear's GraphQL API for advanced integrations:
# Query for team issues with custom fields
query TeamIssues($teamId: String!) {
team(id: $teamId) {
issues {
nodes {
id
title
description
state {
name
type
}
assignee {
email
name
}
labels {
nodes {
name
color
}
}
estimate
cycle {
name
startsAt
endsAt
}
parent {
id
title
}
children {
nodes {
id
title
}
}
}
}
}
}
Platform API Integration
Sync data using platform APIs:
// Sync Linear cycle to platform sprint
async function syncCycleToSprint(cycle) {
const sprint = await platformApi.sprints.create({
name: cycle.name,
start_date: cycle.startsAt,
end_date: cycle.endsAt,
team_id: getTeamMapping(cycle.team.id),
external_id: cycle.id,
external_source: 'linear'
});
// Update all cycle issues to reference the sprint
const issues = await linearApi.issues.list({
filter: { cycle: { id: { eq: cycle.id } } }
});
for (const issue of issues.nodes) {
const task = await findLinkedTask(issue.id);
if (task) {
await platformApi.tasks.update(task.id, {
sprint_id: sprint.id
});
}
}
}
Reporting and Analytics
Cross-Platform Reports
Generate reports combining data from both platforms:
// Generate development velocity report
async function generateVelocityReport(teamId, timeframe) {
const [platformTasks, linearIssues] = await Promise.all([
platformApi.tasks.list({
team_id: teamId,
completed_after: timeframe.start,
completed_before: timeframe.end
}),
linearApi.issues.list({
filter: {
team: { id: { eq: getLinearTeamId(teamId) } },
completedAt: {
gte: timeframe.start,
lte: timeframe.end
}
}
})
]);
return {
platform_velocity: {
tasks_completed: platformTasks.length,
total_hours: platformTasks.reduce((sum, task) => sum + task.actual_hours, 0),
average_completion_time: calculateAverage(platformTasks, 'completion_time')
},
linear_velocity: {
issues_completed: linearIssues.nodes.length,
total_estimate: linearIssues.nodes.reduce((sum, issue) => sum + (issue.estimate || 0), 0),
story_points_per_day: calculateDailyAverage(linearIssues.nodes, timeframe)
},
sync_health: {
synced_percentage: calculateSyncPercentage(platformTasks, linearIssues.nodes),
last_sync_error: await getLastSyncError(teamId)
}
};
}
Troubleshooting
Common Issues
Sync Conflicts
Status Update Conflicts:
// Handle conflicting status updates
async function resolveSyncConflict(taskId, issueId, conflict) {
const resolution = await determinePriority(conflict);
switch (resolution.strategy) {
case 'platform_wins':
await updateLinearIssue(issueId, {
state: mapStatusToLinearState(conflict.platform_status)
});
break;
case 'linear_wins':
await updatePlatformTask(taskId, {
status: mapLinearStateToStatus(conflict.linear_status)
});
break;
case 'manual_review':
await createConflictResolutionTask(taskId, issueId, conflict);
break;
}
}
API Rate Limiting
Linear API Rate Limits:
- 1000 requests per hour per user
- 10 requests per second burst limit
- GraphQL complexity limits
Rate Limit Handling:
const rateLimiter = {
async makeRequest(requestFn) {
try {
return await requestFn();
} catch (error) {
if (error.status === 429) {
const retryAfter = error.headers['retry-after'] * 1000;
await new Promise(resolve => setTimeout(resolve, retryAfter));
return await requestFn();
}
throw error;
}
}
};
Authentication Issues
API Key Validation:
async function validateLinearApiKey(apiKey) {
try {
const response = await fetch('https://api.linear.app/graphql', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: 'query { viewer { id name email } }'
})
});
const data = await response.json();
if (data.errors) {
throw new Error('Invalid API key or insufficient permissions');
}
return data.data.viewer;
} catch (error) {
console.error('Linear API key validation failed:', error);
return null;
}
}
Debugging Tools
Sync Status Monitor
// Monitor synchronization health
async function getSyncHealth(teamMapping) {
const health = {
last_sync: await getLastSyncTime(teamMapping.id),
pending_syncs: await getPendingSyncs(teamMapping.id),
failed_syncs: await getFailedSyncs(teamMapping.id),
success_rate: await getSyncSuccessRate(teamMapping.id),
latency: await getAverageSyncLatency(teamMapping.id)
};
health.status = determineHealthStatus(health);
return health;
}
function determineHealthStatus(health) {
if (health.failed_syncs > 5) return 'critical';
if (health.success_rate < 0.95) return 'degraded';
if (health.latency > 30000) return 'slow';
return 'healthy';
}
Integration Testing
// Test Linear integration end-to-end
async function testLinearIntegration(teamMapping) {
const testResults = [];
try {
// Test 1: Create platform task
const task = await createTestTask(teamMapping.platform_project_id);
testResults.push({ test: 'task_creation', status: 'passed' });
// Test 2: Verify Linear issue creation
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for sync
const issue = await findLinearIssue(task.linear_issue_id);
testResults.push({
test: 'issue_sync',
status: issue ? 'passed' : 'failed'
});
// Test 3: Update issue status in Linear
await updateLinearIssueStatus(issue.id, 'In Progress');
testResults.push({ test: 'status_update', status: 'passed' });
// Test 4: Verify platform task update
await new Promise(resolve => setTimeout(resolve, 5000));
const updatedTask = await getPlatformTask(task.id);
testResults.push({
test: 'status_sync_back',
status: updatedTask.status === 'in-progress' ? 'passed' : 'failed'
});
// Cleanup
await deleteTestTask(task.id);
await deleteLinearIssue(issue.id);
} catch (error) {
testResults.push({
test: 'integration_test',
status: 'failed',
error: error.message
});
}
return testResults;
}
Best Practices
Workflow Optimization
Team Structure Alignment
Map Linear Teams to Platform Projects:
const teamStructure = {
// Frontend team handles UI/UX briefs
'frontend-team': {
linear_team_id: 'team_frontend_123',
platform_projects: ['ui-redesign', 'mobile-app'],
sync_settings: {
auto_create_from_briefs: true,
label_mapping: {
'ui': 'Frontend',
'mobile': 'Mobile'
}
}
},
// Backend team handles API and infrastructure
'backend-team': {
linear_team_id: 'team_backend_456',
platform_projects: ['api-v2', 'infrastructure'],
sync_settings: {
priority_threshold: 'medium', // Only sync medium+ priority tasks
include_technical_details: true
}
}
};
Development Cycle Integration
Sprint/Cycle Synchronization:
// Sync Linear cycles with platform sprints
async function syncCycleWithSprint(cycle) {
const sprint = await platformApi.sprints.findOrCreate({
name: cycle.name,
start_date: cycle.startsAt,
end_date: cycle.endsAt,
team_id: getTeamMapping(cycle.team.id)
});
// Move all cycle issues to sprint
const cycleIssues = await linearApi.issues.list({
filter: { cycle: { id: { eq: cycle.id } } }
});
for (const issue of cycleIssues.nodes) {
const task = await findLinkedTask(issue.id);
if (task) {
await platformApi.tasks.update(task.id, {
sprint_id: sprint.id,
planned_start: cycle.startsAt,
planned_end: cycle.endsAt
});
}
}
}
Data Consistency
Conflict Resolution Strategy
const conflictResolution = {
// Priority-based resolution
priority: {
'critical': 'linear_wins', // Critical issues drive platform updates
'high': 'platform_wins', // High priority tasks drive Linear
'medium': 'last_modified', // Most recent change wins
'low': 'manual_review' // Manual review for low priority
},
// Field-specific resolution
fields: {
'status': 'last_modified',
'assignee': 'linear_wins', // Developers manage assignments
'priority': 'platform_wins', // Product manages priorities
'estimate': 'linear_wins', // Developers manage estimates
'description': 'platform_wins' // Product manages descriptions
}
};
Data Validation
// Validate sync data before applying changes
function validateSyncData(platformTask, linearIssue) {
const validations = [
{
name: 'title_length',
test: () => platformTask.title.length <= 255,
message: 'Task title exceeds Linear title limit'
},
{
name: 'assignee_exists',
test: async () => await linearApi.users.find(platformTask.assignee_email),
message: 'Assignee not found in Linear workspace'
},
{
name: 'valid_status',
test: () => mapStatusToLinearState(platformTask.status) !== null,
message: 'Platform status cannot be mapped to Linear state'
}
];
return Promise.all(validations.map(async validation => ({
name: validation.name,
passed: await validation.test(),
message: validation.message
})));
}
Next Steps
Your Linear integration is now set up! Here's what you can do next:
- Test the Integration: Create a test task in your platform and verify it appears in Linear
- Configure Team Workflows: Set up your team's specific sync preferences and field mappings
- Set up Notifications: Configure Slack Integration to get notified about Linear activity
- Monitor Sync Health: Use the integration dashboard to monitor synchronization status
- Explore Automation: Set up advanced project workflows through team settings and integrations
For questions or advanced configuration needs, check out our Integration Support Center or join the Developer Community.