Installation
npm install @chat-adapter/linear
Environment Variables
| Variable | Required | Description |
|---|
LINEAR_API_KEY | API Key mode | Personal API key from Linear |
LINEAR_ACCESS_TOKEN | OAuth mode | OAuth access token |
LINEAR_CLIENT_ID | App mode | OAuth client ID |
LINEAR_CLIENT_SECRET | App mode | OAuth client secret |
LINEAR_WEBHOOK_SECRET | Yes | Webhook secret for signature verification |
LINEAR_BOT_USERNAME | Optional | Bot username for display |
Configuration Options
type LinearAdapterConfig =
| LinearAdapterAPIKeyConfig
| LinearAdapterOAuthConfig
| LinearAdapterAppConfig;
// Personal API Key
interface LinearAdapterAPIKeyConfig {
apiKey: string;
webhookSecret: string;
userName: string;
logger: Logger;
}
// OAuth Access Token
interface LinearAdapterOAuthConfig {
accessToken: string;
webhookSecret: string;
userName: string;
logger: Logger;
}
// Client Credentials (OAuth App)
interface LinearAdapterAppConfig {
clientId: string;
clientSecret: string;
webhookSecret: string;
userName: string;
logger: Logger;
}
Setup
API Key
OAuth Access Token
Client Credentials
For personal projects or testing:import { Chat } from 'chat';
import { createLinearAdapter } from '@chat-adapter/linear';
import { MemoryState } from '@chat-adapter/state-memory';
const chat = new Chat({
userName: 'my-bot',
adapters: {
linear: createLinearAdapter({
apiKey: process.env.LINEAR_API_KEY!,
webhookSecret: process.env.LINEAR_WEBHOOK_SECRET!,
}),
},
state: new MemoryState(),
});
await chat.initialize();
For user-authorized bots:import { Chat } from 'chat';
import { createLinearAdapter } from '@chat-adapter/linear';
import { MemoryState } from '@chat-adapter/state-memory';
const chat = new Chat({
userName: 'my-bot',
adapters: {
linear: createLinearAdapter({
accessToken: process.env.LINEAR_ACCESS_TOKEN!,
webhookSecret: process.env.LINEAR_WEBHOOK_SECRET!,
}),
},
state: new MemoryState(),
});
await chat.initialize();
For OAuth apps (tokens auto-refresh):import { Chat } from 'chat';
import { createLinearAdapter } from '@chat-adapter/linear';
import { MemoryState } from '@chat-adapter/state-memory';
const chat = new Chat({
userName: 'my-bot',
adapters: {
linear: createLinearAdapter({
clientId: process.env.LINEAR_CLIENT_ID!,
clientSecret: process.env.LINEAR_CLIENT_SECRET!,
webhookSecret: process.env.LINEAR_WEBHOOK_SECRET!,
}),
},
state: new MemoryState(),
});
await chat.initialize();
Client credentials tokens are valid for 30 days and auto-refresh when expired.
Webhook Handler
app.post('/webhooks/linear', async (req, res) => {
const response = await linear.handleWebhook(req, {
waitUntil: (promise) => {/* handle async work */},
});
res.status(response.status).send(await response.text());
});
Configure webhook in Linear:
- Go to Settings → Workspace → Webhooks
- Create webhook with URL:
https://your-app.com/webhooks/linear
- Add webhook secret
- Subscribe to events:
Comment (create), Reaction (create/delete)
Features
Supported Events
Comment (create) - New comments on issues
Reaction (create/delete) - Emoji reactions on comments
Thread Types
Linear adapter supports two thread types:
1. Issue-level threads
All top-level comments on an issue.
2. Comment-level threads
linear:{issueId}:c:{commentId}
Replies to a specific comment (nested thread).
Post to issue:
// Thread ID: linear:ISS-123
await thread.post('This looks good to me!');
Reply to a comment thread:
// Thread ID: linear:ISS-123:c:comment-abc
await thread.post('Thanks for the clarification.');
Markdown Support
Linear supports markdown in comments:
await thread.post(`
## Update
Fixed the following:
- [x] Bug in login flow
- [x] Performance issue
- [ ] Documentation
`);
Reactions
Add reactions to comments:
await thread.addReaction(commentId, '👍');
await thread.addReaction(commentId, { name: 'thumbs_up' });
removeReaction() is not fully supported. Linear requires the reaction ID, which would need an additional API call to look up.
Cards
Cards are converted to markdown:
import { Card, Section } from 'chat/cards';
await thread.post(
<Card title="Status Update">
<Section text="All tasks completed for this sprint" />
</Card>
);
// Renders as formatted markdown
Thread IDs
Linear thread IDs encode issue ID and optional comment ID:
linear:{issueId}[:c:{commentId}]
Examples:
- Issue:
linear:PROJ-123
- Comment thread:
linear:PROJ-123:c:abc123
Message History
Fetch comments from an issue or comment thread:
const { messages, nextCursor } = await thread.fetchMessages({
limit: 50,
direction: 'backward',
});
Threading Behavior
Linear webhooks include parent comment information:
- Root comment (no
parentId): Creates new comment-level thread
- Reply (has
parentId): Routes to parent’s thread
This ensures all replies to a comment are grouped together.
- Comment length: ~10,000 characters (no official limit documented)
- API rate limits: Linear rate limits
Code Examples
chat.onNewMessage(async (event) => {
if (event.message.text.includes('approved')) {
await event.thread.addReaction(event.message.id, '✅');
}
});
Troubleshooting
- Verify
LINEAR_WEBHOOK_SECRET matches Linear webhook settings
- Check webhook timestamp is within 5 minutes (prevent replay attacks)
- Ensure request body is raw (not parsed)
Bot doesn't respond to comments
- Grant
comments:create scope for adding reactions
- Use Unicode emoji strings or emoji names
- Note:
removeReaction() has limited support
Client credentials token expired
- Adapter auto-refreshes tokens before expiry
- If manual refresh needed, restart the adapter
- Tokens are valid for 30 days
Required Scopes
Linear OAuth Scopes:
read - Read issues and comments
write - Create/edit comments
comments:create - Create comments
issues:create - Create issues (if needed)
Personal API keys have full access to your workspace.
Creating a Linear OAuth App
Configure scopes
Select required scopes:
read
write
comments:create
Get credentials
Copy Client ID and Client Secret.
Set redirect URL
Add your OAuth callback URL (if using user authorization flow).
Next Steps
Message Handling
Process comments and build workflows
Linear SDK
Learn about the Linear GraphQL API
Issue Automation
Build automated issue management bots
State Management
Persist data across issue comments