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

# Linear

> Query and mutate Linear issues, teams, projects, labels, workflow states, priorities, and comments from your backend.

`LinearService` is a container-managed wrapper around the official `@linear/sdk` client. It exposes a typed, promise-based API for reading and writing Linear issues, teams, projects, labels, workflow states, priorities, and comments, and it normalizes Linear's GraphQL objects into plain Talos types (`Issue`, `LinearLabelType`, `LinearStateType`, and friends) so call sites never touch the SDK directly. Configuration comes from either the constructor or `AppEnv`, and every failure is surfaced as a `LinearException` with a machine-readable `key`.

## Installation

`LinearService` ships as its own package and depends on the Linear SDK.

```bash theme={null}
bun add @talosjs/linear
```

## Configuration

The service needs a Linear API key. Provide it through the constructor config or expose it through `AppEnv` as `LINEAR_API_KEY`. An optional default team id can be set the same way, so team-scoped calls don't need a `teamId` argument.

```typescript theme={null}
import { AppEnv } from "@talosjs/app-env";
import { LinearService } from "@talosjs/linear";

const linear = new LinearService(new AppEnv(), {
  apiKey: "lin_api_...",
  teamId: "team-id",
});
```

The API key is validated in the constructor, so a missing key fails fast at startup with `API_KEY_REQUIRED`. Generate a personal API key from your Linear settings under **Security & access → Personal API keys**.

## Environment variables

| Variable         | Required | Purpose                                                                      |
| ---------------- | -------- | ---------------------------------------------------------------------------- |
| `LINEAR_API_KEY` | Yes      | Linear API key. Missing throws `LinearException` (`API_KEY_REQUIRED`).       |
| `LINEAR_TEAM_ID` | No       | Default team id for team-scoped reads and writes when no `teamId` is passed. |

```bash theme={null}
LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxx
LINEAR_TEAM_ID=team-id
```

Config passed to the constructor takes precedence over the environment: `config.apiKey || env.LINEAR_API_KEY` and `config.teamId || env.LINEAR_TEAM_ID`.

## Usage

Resolve the service from the container and call its methods. Reads return normalized types; writes return the updated entity.

```typescript theme={null}
import { container } from "@talosjs/container";
import { LinearService } from "@talosjs/linear";

const linear = container.get(LinearService);

// Read a single issue by id or identifier (e.g. "OO-123")
const issue = await linear.getIssue("OO-123");

// Create an issue — title and team.id are required
const created = await linear.createIssue({
  title: "Document Linear package",
  description: "Add public package usage documentation.",
  team: { id: "team-id", name: "Engineering", key: "ENG" },
});

// Comment on it
await linear.createComment(created.id, "Documentation added.");
```

Inject it into a service to drive Linear as part of your domain logic:

```typescript theme={null}
import { inject } from "@talosjs/container";
import { LinearService } from "@talosjs/linear";

export class SupportService {
  constructor(
    @inject(LinearService) private readonly linear: LinearService,
  ) {}

  public async escalate(summary: string, details: string) {
    const issue = await this.linear.createIssue({
      title: summary,
      description: details,
      team: { id: "team-id", name: "Engineering", key: "ENG" },
    });
    await this.linear.setPriority(issue.id, 1); // Urgent
    return issue;
  }
}
```

## API

### Issues

| Method                         | Purpose                                                       |
| ------------------------------ | ------------------------------------------------------------- |
| `getIssue(id)`                 | Fetch one issue by id or identifier.                          |
| `getIssues(teamId?, filters?)` | Fetch issues, optionally scoped to a team and Linear filters. |
| `createIssue(input)`           | Create an issue. `title` and `team.id` are required.          |
| `updateIssue(id, input)`       | Update issue fields.                                          |
| `deleteIssue(id)`              | Delete an issue; returns Linear's success flag.               |

When `teamId` is omitted from `getIssues`, the default team id from config or `LINEAR_TEAM_ID` is used.

### Teams, projects, and the viewer

| Method                 | Purpose                                           |
| ---------------------- | ------------------------------------------------- |
| `getTeams()`           | List available teams.                             |
| `getProjects(teamId?)` | List projects, optionally filtered by team.       |
| `getViewer()`          | Return the authenticated user behind the API key. |

### Labels

| Method                            | Purpose                           |
| --------------------------------- | --------------------------------- |
| `getLabel(id)`                    | Fetch one label.                  |
| `getLabels(teamId?)`              | List labels, optionally by team.  |
| `createLabel(input)`              | Create a label; `name` required.  |
| `updateLabel(id, input)`          | Update a label.                   |
| `deleteLabel(id)`                 | Delete a label.                   |
| `checkLabelById(id)`              | Test whether a label id exists.   |
| `checkLabelByName(name, teamId?)` | Test whether a label name exists. |

### Workflow states

| Method                            | Purpose                                                     |
| --------------------------------- | ----------------------------------------------------------- |
| `getState(id)`                    | Fetch one workflow state.                                   |
| `getStates(teamId?)`              | List workflow states, optionally by team.                   |
| `createState(input)`              | Create a state; `name`, `color`, `type`, and team required. |
| `updateState(id, input)`          | Update a state.                                             |
| `deleteState(id)`                 | Archive a state; returns Linear's success flag.             |
| `checkStateById(id)`              | Test whether a state id exists.                             |
| `checkStateByName(name, teamId?)` | Test whether a state name exists.                           |

### Priorities

Linear priorities are a fixed scale. The service exposes them synchronously and validates any value you set against the scale.

| Value | Label       |
| ----- | ----------- |
| `0`   | No priority |
| `1`   | Urgent      |
| `2`   | High        |
| `3`   | Normal      |
| `4`   | Low         |

| Method                           | Purpose                                          |
| -------------------------------- | ------------------------------------------------ |
| `getPriorities()`                | Return the full priority scale (synchronous).    |
| `getPriority(issueId)`           | Return the priority of an issue.                 |
| `setPriority(issueId, priority)` | Set an issue's priority; rejects invalid values. |
| `clearPriority(issueId)`         | Reset an issue's priority to `0` (No priority).  |
| `checkPriorityById(value)`       | Test whether a numeric priority is valid.        |
| `checkPriorityByName(name)`      | Test whether a priority label is valid.          |

### Comments

| Method                         | Purpose                    |
| ------------------------------ | -------------------------- |
| `createComment(issueId, body)` | Add a comment to an issue. |

## Use in the app

In an `@talosjs/app` application, Linear isn't a dedicated `App` config slot. `LinearService` is `@injectable()`, so it registers with the container as soon as the class is imported, and you resolve or inject it wherever you talk to Linear.

```bash theme={null}
bun add @talosjs/app @talosjs/linear
```

Set `LINEAR_API_KEY` (and optionally `LINEAR_TEAM_ID`) in your `.env.yml` (or environment) so the client can initialize, then inject the service into a controller or service:

```typescript theme={null}
import { inject } from "@talosjs/container";
import { LinearService } from "@talosjs/linear";

export class ReleaseService {
  constructor(
    @inject(LinearService) private readonly linear: LinearService,
  ) {}

  public async openReleaseTicket(version: string) {
    return this.linear.createIssue({
      title: `Release ${version}`,
      team: { id: "team-id", name: "Engineering", key: "ENG" },
    });
  }
}
```

The CLI can scaffold your Linear credentials into `.env.yml` for you — see [`linear:credentials:create`](/cli/commands/linear-credentials-create).

## Exceptions

Every method throws `LinearException` on failure, carrying a machine-readable `key` and a `data` payload with the relevant ids and the original `cause`. All exceptions map to an `InternalServerError` status.

| Key                                                                | When                                              |
| ------------------------------------------------------------------ | ------------------------------------------------- |
| `API_KEY_REQUIRED`                                                 | The service is constructed without an API key.    |
| `ISSUE_FETCH_ERROR`                                                | `getIssue` fails.                                 |
| `ISSUES_FETCH_ERROR`                                               | `getIssues` fails.                                |
| `ISSUE_CREATE_ERROR`                                               | `createIssue` fails or is missing `title`/`team`. |
| `ISSUE_UPDATE_ERROR`                                               | `updateIssue` fails.                              |
| `ISSUE_DELETE_ERROR`                                               | `deleteIssue` fails.                              |
| `TEAMS_FETCH_ERROR`                                                | `getTeams` fails.                                 |
| `PROJECTS_FETCH_ERROR`                                             | `getProjects` fails.                              |
| `VIEWER_FETCH_ERROR`                                               | `getViewer` fails.                                |
| `LABEL_FETCH_ERROR` / `LABELS_FETCH_ERROR`                         | Label reads fail.                                 |
| `LABEL_CREATE_ERROR` / `LABEL_UPDATE_ERROR` / `LABEL_DELETE_ERROR` | Label writes fail.                                |
| `LABEL_CHECK_ERROR`                                                | `checkLabelByName` fails.                         |
| `STATE_FETCH_ERROR` / `STATES_FETCH_ERROR`                         | State reads fail.                                 |
| `STATE_CREATE_ERROR` / `STATE_UPDATE_ERROR` / `STATE_DELETE_ERROR` | State writes fail.                                |
| `STATE_CHECK_ERROR`                                                | `checkStateByName` fails.                         |
| `PRIORITY_FETCH_ERROR`                                             | `getPriority` fails or returns an unknown value.  |
| `PRIORITY_SET_ERROR`                                               | `setPriority` fails or is given an invalid value. |
| `COMMENT_CREATE_ERROR`                                             | `createComment` fails.                            |

```typescript theme={null}
import { LinearException } from "@talosjs/linear";

try {
  const issue = await linear.getIssue("OO-123");
} catch (error) {
  if (error instanceof LinearException && error.key === "ISSUE_FETCH_ERROR") {
    logger.error("Could not load the Linear issue", error.data);
  }
  throw error;
}
```

## Types

The package exports `Issue`, `LinearService`, `LinearException`, the `ILinearService` interface, `LinearConfigType`, and the supporting payload types (`LinearTeamType`, `LinearProjectType`, `LinearUserType`, `LinearLabelType`, `LinearStateType`, `LinearPriorityType`, `LinearCommentType`) from `@talosjs/linear`.
