linear-install-auth

Install and configure Linear SDK/CLI authentication. Use when setting up a new Linear integration, configuring API keys, OAuth2 flows, or initializing LinearClient in your project. Trigger: "install linear", "setup linear", "linear auth", "configure linear API key", "linear SDK setup", "linear OAuth".

claude-codecodexopenclaw
7 Tools
linear-pack Plugin
saas packs Category

Allowed Tools

ReadWriteEditBash(npm:*)Bash(pnpm:*)Bash(yarn:*)Grep

Provided by Plugin

linear-pack

Claude Code skill pack for Linear (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the linear-pack plugin:

/plugin install linear-pack@claude-code-plugins-plus

Click to copy

Instructions

Linear Install & Auth

Overview

Install the @linear/sdk TypeScript SDK and configure authentication for the Linear GraphQL API at https://api.linear.app/graphql. Supports personal API keys for scripts and OAuth 2.0 (with PKCE) for user-facing apps.

Prerequisites

  • Node.js 18+ (SDK is TypeScript-first, works in any JS environment)
  • Package manager (npm, pnpm, or yarn)
  • Linear account with workspace access
  • For API key: Settings > Account > API > Personal API keys
  • For OAuth: Create app at Settings > Account > API > OAuth applications

Instructions

Step 1: Install the SDK


set -euo pipefail
npm install @linear/sdk
# or: pnpm add @linear/sdk
# or: yarn add @linear/sdk

The SDK exposes LinearClient, typed models for every entity (Issue, Project, Cycle, Team), and error classes (LinearError, InvalidInputLinearError).

Step 2: API Key Authentication (Scripts & Server-Side)

Generate a Personal API key at Linear Settings > Account > API > Personal API keys. Keys start with linapi and are shown only once.


import { LinearClient } from "@linear/sdk";

// Environment variable (recommended)
const client = new LinearClient({
  apiKey: process.env.LINEAR_API_KEY,
});

// Verify connection
const me = await client.viewer;
console.log(`Authenticated as: ${me.name} (${me.email})`);

Environment setup:


# .env (never commit)
LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

# .gitignore
echo '.env' >> .gitignore

Step 3: OAuth 2.0 Authentication (User-Facing Apps)

Linear supports the standard Authorization Code flow with optional PKCE. As of October 2025, newly created OAuth apps issue refresh tokens by default.


import crypto from "crypto";

// 1. Build authorization URL
const SCOPES = ["read", "write", "issues:create"];
const state = crypto.randomBytes(16).toString("hex");

const authUrl = new URL("https://linear.app/oauth/authorize");
authUrl.searchParams.set("client_id", process.env.LINEAR_CLIENT_ID!);
authUrl.searchParams.set("redirect_uri", process.env.LINEAR_REDIRECT_URI!);
authUrl.searchParams.set("response_type", "code");
authUrl.searchParams.set("scope", SCOPES.join(","));
authUrl.searchParams.set("state", state);
// Optional PKCE:
// authUrl.searchParams.set("code_challenge", challenge);
// authUrl.searchParams.set("code_challenge_method", "S256");

// 2. Exchange authorization code for tokens
const tokenResponse = await fetch("https://api.linear.app/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code: authorizationCode,
    client_id: process.env.LINEAR_CLIENT_ID!,
    client_secret: process.env.LINEAR_CLIENT_SECRET!,
    redirect_uri: process.env.LINEAR_REDIRECT_URI!,
  }),
});

const { access_token, refresh_token, expires_in } = await tokenResponse.json();

// 3. Create client with OAuth token
const client = new LinearClient({ accessToken: access_token });

Available OAuth scopes: read, write, issues:create, admin, initiative:read, initiative:write, customer:read, customer:write.

Step 4: Token Refresh (OAuth)


async function refreshAccessToken(refreshToken: string): Promise<string> {
  const response = await fetch("https://api.linear.app/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: refreshToken,
      client_id: process.env.LINEAR_CLIENT_ID!,
      client_secret: process.env.LINEAR_CLIENT_SECRET!,
    }),
  });

  if (!response.ok) {
    throw new Error(`Token refresh failed: ${response.status}`);
  }

  const tokens = await response.json();
  // Store new refresh_token — Linear rotates it on each refresh
  await saveTokens(tokens.access_token, tokens.refresh_token);
  return tokens.access_token;
}

Step 5: Validate Configuration on Startup


function validateLinearConfig(): void {
  const key = process.env.LINEAR_API_KEY;
  if (!key) throw new Error("LINEAR_API_KEY environment variable is required");
  if (!key.startsWith("lin_api_")) throw new Error("LINEAR_API_KEY must start with lin_api_");
  if (key.length < 30) throw new Error("LINEAR_API_KEY appears truncated");
}

// Call before creating client
validateLinearConfig();

Error Handling

Error Cause Solution
Authentication required Invalid, expired, or missing API key Regenerate at Settings > Account > API
Invalid API key format Key doesn't start with linapi Copy full key from Linear (shown once)
Forbidden Token lacks required OAuth scope Re-authorize with correct scopes
invalid_grant on token exchange Code expired or PKCE verifier mismatch Restart OAuth flow; codes expire quickly
Module not found: @linear/sdk SDK not installed Run npm install @linear/sdk
ENOTFOUND api.linear.app DNS/firewall issue Ensure outbound HTTPS to api.linear.app

Examples

Raw GraphQL Without SDK


const response = await fetch("https://api.linear.app/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": process.env.LINEAR_API_KEY!,
  },
  body: JSON.stringify({
    query: `{ viewer { id name email } }`,
  }),
});

const { data, errors } = await response.json();
if (errors) console.error("GraphQL errors:", errors);
else console.log("Viewer:", data.viewer);

Server-to-Server (Client Credentials)


// For apps without user interaction — uses client_credentials grant
const response = await fetch("https://api.linear.app/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "client_credentials",
    client_id: process.env.LINEAR_CLIENT_ID!,
    client_secret: process.env.LINEAR_CLIENT_SECRET!,
    scope: "read,write",
  }),
});

const { access_token } = await response.json();
const client = new LinearClient({ accessToken: access_token });

Resources

Ready to use linear-pack?