Skip to main content
The Linear integration enables bidirectional synchronization between Linear issues and Bounty bounties, with automatic comment notifications.

Overview

Connect your Linear workspace to:
  • Import issues as bounties
  • Sync issue metadata (title, description, status)
  • Post automatic comments on Linear issues when bounties are created or updated
  • Filter and browse Linear issues directly in Bounty

Prerequisites

  • A Linear workspace with admin access
  • Linear OAuth application credentials

OAuth Setup

1

Create Linear OAuth Application

Visit the Linear Applications page and create a new OAuth application.Configure the following:
  • Application name: Your Bounty instance name
  • Callback URL: https://your-domain.com/api/auth/callback/linear
  • Scopes: read, write (for issues and comments)
2

Get Client Credentials

After creating the application, Linear provides:
  • Client ID
  • Client Secret
Keep these secure - they’ll be added to your environment variables.
3

Configure Environment Variables

Add to your .env file:
# Linear OAuth
LINEAR_CLIENT_ID="your_linear_client_id"
LINEAR_CLIENT_SECRET="your_linear_client_secret"

Connecting Your Workspace

Once OAuth is configured, users can connect their Linear workspace:
1

Navigate to Integrations

In your Bounty organization settings, find the Linear integration section.
2

Click Connect Linear

You’ll be redirected to Linear’s OAuth authorization page.
3

Authorize Access

Grant Bounty permission to:
  • Read issues and projects
  • Create comments on issues
  • Access workspace metadata
4

Sync Workspace

After authorization, Bounty automatically:
  • Fetches workspace information
  • Syncs teams and projects
  • Stores the connection for your organization

Token Management

Bounty automatically handles Linear OAuth token refresh:
  • Access tokens expire after a set period (typically 10 hours)
  • Refresh tokens are used to obtain new access tokens automatically
  • Tokens are refreshed 5 minutes before expiration
// Automatic token refresh (handled internally)
if (isTokenExpired(accessToken)) {
  const newToken = await refreshLinearToken(refreshToken);
  // Updates stored in database automatically
}
If a refresh fails, users will need to reconnect their Linear account through the OAuth flow.

Working with Issues

Fetching Issues

API endpoint: linear.getIssues Retrieve issues with optional filters:
const { issues, hasNextPage, endCursor } = await trpc.linear.getIssues.query({
  filters: {
    status: ['In Progress', 'Todo'],
    priority: [1, 2],  // 1 = Urgent, 2 = High
    assigneeId: 'user_id_here',
    projectId: 'project_id_here',
  },
  pagination: {
    first: 50,  // Max 100
    after: endCursor,  // For next page
  },
});

Get Single Issue

API endpoint: linear.getIssue
const { issue } = await trpc.linear.getIssue.query({
  issueId: 'ISS-123',
});

// Returns: title, description, status, assignee, priority, etc.

Creating Bounties from Issues

API endpoint: linear.getBountyDataFromIssue Pre-fill bounty data from a Linear issue:
const { data } = await trpc.linear.getBountyDataFromIssue.query({
  linearIssueId: 'abc123',
});

// Returns pre-filled bounty fields:
// - title: Issue title
// - description: Issue description
// - linearIssueId: Unique issue ID
// - linearIssueIdentifier: Human-readable (e.g., "ENG-42")
// - linearIssueUrl: Direct link to issue

Automatic Comments

When bounty events occur, Bounty automatically posts comments to linked Linear issues.

Bounty Created

Posted when a bounty is first created:
🎯 Bounty Created: $500 USD

A bounty has been posted for this issue.

View details: https://bounty.new/bounty/abc123
API usage:
await trpc.linear.postComment.mutate({
  linearIssueId: 'issue_abc123',
  commentType: 'bountyCreated',
  bountyData: {
    title: 'Fix authentication bug',
    amount: '500',
    currency: 'USD',
    bountyUrl: 'https://bounty.new/bounty/abc123',
  },
});

Bounty Funded

Posted when payment is received:
✅ Bounty Funded: $500 USD

This bounty is now funded and ready for work!

Deadline: 2026-04-01
Submit solution: https://bounty.new/bounty/abc123
await trpc.linear.postComment.mutate({
  linearIssueId: 'issue_abc123',
  commentType: 'bountyFunded',
  bountyData: {
    amount: '500',
    currency: 'USD',
    bountyUrl: 'https://bounty.new/bounty/abc123',
    deadline: '2026-04-01',  // Optional
  },
});

Submission Received

Posted when a contributor submits work:
📬 Submission Received

User @contributor submitted a solution on 2026-03-04 10:30 AM

Review submission: https://bounty.new/bounty/abc123
await trpc.linear.postComment.mutate({
  linearIssueId: 'issue_abc123',
  commentType: 'submissionReceived',
  bountyData: {
    bountyUrl: 'https://bounty.new/bounty/abc123',
    submitter: 'contributor',
    timestamp: '2026-03-04T10:30:00Z',
  },
});

Bounty Completed

Posted when a bounty is awarded:
🎉 Bounty Completed

Winner: @contributor
Completed: 2026-03-05 14:00 PM

View details: https://bounty.new/bounty/abc123
await trpc.linear.postComment.mutate({
  linearIssueId: 'issue_abc123',
  commentType: 'bountyCompleted',
  bountyData: {
    bountyUrl: 'https://bounty.new/bounty/abc123',
    winner: 'contributor',
    timestamp: '2026-03-05T14:00:00Z',
  },
});

Workspace Information

Get Workspaces

API endpoint: linear.getWorkspaces Retrieve connected workspaces for the active organization:
const { workspaces } = await trpc.linear.getWorkspaces.query();

// Each workspace includes:
// - id: Workspace ID
// - name: Workspace name
// - key: Workspace key (e.g., "ENG")
// - url: Workspace URL

Get Teams

API endpoint: linear.getTeams
const { teams } = await trpc.linear.getTeams.query();

// Returns: id, name, key

Get Workflow States

API endpoint: linear.getWorkflowStates
const { states } = await trpc.linear.getWorkflowStates.query();

// Returns: id, name, type (e.g., 'started', 'completed', 'canceled')

Get Projects

API endpoint: linear.getProjects
const { projects } = await trpc.linear.getProjects.query();

// Returns: id, name, description, state

Disconnecting Linear

To disconnect a Linear workspace:
await trpc.linear.disconnect.mutate({
  workspaceId: 'workspace_abc123',
});
This will:
  1. Delete the workspace connection record
  2. Remove OAuth tokens if no other workspaces are connected
  3. Preserve existing bounties (they lose Linear sync but remain accessible)
Only organization owners can disconnect Linear workspaces.

Rate Limiting

Linear API operations are rate-limited per organization:
  • Read operations: Scoped to linear:read rate limit
  • Write operations (comments): Scoped to linear:comment rate limit
  • Create operations: Scoped to linear:create rate limit
If you exceed rate limits, requests will be temporarily blocked.

Troubleshooting

”Linear account not connected”

Solution: User needs to connect their Linear account via OAuth:
  1. Go to organization settings
  2. Click “Connect Linear”
  3. Authorize access on Linear

”No access token available”

Cause: OAuth token was invalidated or expired without a refresh token. Solution: Reconnect Linear through the OAuth flow.

”Failed to refresh Linear token”

Causes:
  • Refresh token expired
  • Linear OAuth app credentials changed
  • User revoked access in Linear settings
Solution:
  1. Check LINEAR_CLIENT_ID and LINEAR_CLIENT_SECRET are correct
  2. Reconnect the workspace through OAuth

Comments Not Posting

Checklist:
  • Ensure Linear OAuth app has write scope
  • Verify the linearIssueId is valid
  • Check that the user’s access token is valid
  • Confirm the issue exists and is accessible

API Reference

getWorkspaces

Returns all Linear workspaces connected to the active organization. Returns: { workspaces: Array<{ id, name, key, url }> }

getConnectionStatus

Check if Linear is connected for the active organization. Returns: { connected: boolean, workspace: WorkspaceInfo | null }

getIssues

Input: { filters?: {...}, pagination?: {...} } Returns: { issues: Issue[], hasNextPage: boolean, endCursor: string }

getIssue

Input: { issueId: string } Returns: { issue: Issue }

postComment

Input: Discriminated union based on commentType:
  • bountyCreated
  • bountyFunded
  • submissionReceived
  • bountyCompleted
Returns: { success: boolean, commentId: string | null }

disconnect

Input: { workspaceId: string } Requires: Organization owner role Returns: { success: boolean }

syncWorkspace

Called automatically after OAuth. Fetches workspace info and creates the connection record. Returns: { success: boolean, workspace: WorkspaceInfo | null }

Build docs developers (and LLMs) love