Linear
Automate Linear issue comment threads with bot responses. Supports both standard comments mode and Linear's app-actor agent sessions.
Install
pnpm add @chat-adapter/linearQuick start
The adapter auto-detects credentials from LINEAR_API_KEY, LINEAR_ACCESS_TOKEN, LINEAR_CLIENT_CREDENTIALS_*, or LINEAR_CLIENT_ID/LINEAR_CLIENT_SECRET, plus LINEAR_WEBHOOK_SECRET and LINEAR_BOT_USERNAME.
import { Chat } from "chat";
import { createLinearAdapter } from "@chat-adapter/linear";
const bot = new Chat({
userName: "my-bot",
adapters: {
linear: createLinearAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post("Hello from Linear!");
});By default, the adapter runs in mode: "comments" and treats Comment webhooks as the inbound message source. For Linear app-actor installs, set mode: "agent-sessions" so inbound handling is driven by AgentSessionEvent.
Configuration
Prop
Type
One of apiKey, accessToken, top-level clientId/clientSecret, or clientCredentials is required, plus webhookSecret.
Authentication
Option A — Personal API key
Best for personal projects or single-workspace bots. Actions are attributed to you as an individual.
- Go to Settings then Security & Access.
- Under Personal API keys, click Create key.
- Choose Only select permissions and enable Create issues + Create comments.
- Set
LINEAR_API_KEY.
createLinearAdapter({ apiKey: process.env.LINEAR_API_KEY! });Option B — OAuth access token
Use this when your app already manages the OAuth flow:
createLinearAdapter({ accessToken: process.env.LINEAR_ACCESS_TOKEN! });Option C — Multi-tenant OAuth installs
Use top-level clientId / clientSecret for Slack-style multi-tenant installs. Each Linear workspace install is stored separately, webhook requests resolve the correct workspace token by organizationId, and withInstallation() lets you target a specific organization outside webhook handling.
- Go to Settings then API then Applications.
- Create the OAuth2 application.
- Note the Client ID and Client Secret.
const adapter = createLinearAdapter({
clientId: process.env.LINEAR_CLIENT_ID!,
clientSecret: process.env.LINEAR_CLIENT_SECRET!,
mode: "agent-sessions",
});
await bot.initialize();
const { organizationId } = await adapter.handleOAuthCallback(request, {
redirectUri: process.env.LINEAR_REDIRECT_URI!,
});
await adapter.withInstallation(organizationId, async () => {
await adapter.postMessage("linear:issue-id", "Hello from a background job");
});Option D — Single-tenant client credentials
App identity without multi-tenant installs. The adapter fetches and refreshes the token automatically.
createLinearAdapter({
clientCredentials: {
clientId: process.env.LINEAR_CLIENT_CREDENTIALS_CLIENT_ID!,
clientSecret: process.env.LINEAR_CLIENT_CREDENTIALS_CLIENT_SECRET!,
scopes: ["read", "write", "comments:create", "issues:create"],
},
mode: "agent-sessions",
});Advanced
Token encryption
For multi-tenant OAuth installs, pass a base64-encoded 32-byte key as encryptionKey (or set LINEAR_ENCRYPTION_KEY) to encrypt stored access and refresh tokens at rest:
openssl rand -base64 32When encryptionKey is set, setInstallation() encrypts tokens before writing to the configured state adapter. Existing plaintext records continue to work — you can roll the key in without flushing installs.
Making the bot @-mentionable
To make the bot appear in Linear's @-mention dropdown as an Agent:
- In your OAuth app settings, enable Agent session events under webhooks.
- Have a workspace admin install the app with
actor=appand theapp:mentionablescope:
https://linear.app/oauth/authorize?
client_id=your_client_id&
redirect_uri=https://your-domain.com/callback&
response_type=code&
scope=read,write,comments:create,issues:create,app:mentionable&
actor=appOnce installed with actor=app, set mode: "agent-sessions" so the adapter treats AgentSessionEvent as the entrypoint:
onNewMentionfires from session-created events.thread.startTyping()sends an ephemeral Linearthought.thread.post(stream)uses agent activities and session plan updates.- Session threads are append-only;
sent.edit()/sent.delete()are not supported there.
See the Linear Agents docs for full details.
Direct API client
Access the underlying LinearClient via .linearClient:
const linear = bot.getAdapter("linear").linearClient;
const issue = await linear.issue("ENG-123");API key, access token, and single-tenant client-credentials modes return the same client anywhere. Multi-tenant OAuth requires webhook-handler context.
The previous
.clientgetter still works as a deprecated alias for.linearClient.
Webhook setup
Webhook management requires workspace admin access. If you don't see the API settings page, ask a workspace admin.
- Go to Settings then API then click Create webhook.
- Set the URL to
https://your-domain.com/api/webhooks/linear. - Copy the Signing secret to
LINEAR_WEBHOOK_SECRET. - Under Data change events, select Comments (required for
mode: "comments"), Agent session events (required formode: "agent-sessions"), Issues, and optionally Emoji reactions. - Choose a team selection and click Create webhook.
Thread model
Linear has four thread variants:
| Type | Description | Thread ID format |
|---|---|---|
| Issue-level | Top-level comments on an issue | linear:{issueId} |
| Comment thread | Replies nested under a comment | linear:{issueId}:c:{commentId} |
| Agent session on issue | App-actor session on an issue | linear:{issueId}:s:{agentSessionId} |
| Agent session on comment thread | App-actor session on a comment thread | linear:{issueId}:c:{commentId}:s:{agentSessionId} |
When a user writes a comment, the bot replies within the same comment thread.
Feature support
Messaging
| Feature | Supported |
|---|---|
| Post message | |
| Edit message | Partial |
| Delete message | Partial |
| File uploads | |
| Streaming | Agent sessions / Post+Edit |
| Scheduled messages |
Rich content
| Feature | Supported |
|---|---|
| Card format | Markdown |
| Buttons | |
| Link buttons | |
| Select menus | |
| Tables | GFM |
| Fields | |
| Images in cards | |
| Modals |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | |
| Mentions | |
| Add reactions | |
| Remove reactions | Partial |
| Typing indicator | Agent sessions |
| DMs | |
| Ephemeral messages | |
| User lookup | |
| Parent subject | |
| Native client | |
| Custom API endpoint |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | |
| Fetch single message | |
| Fetch thread info | |
| Fetch channel messages | |
| List threads | |
| Fetch channel info | |
| Post channel message |