Manage Linear issues, projects, and cycles via GraphQL. Use when triaging backlogs, creating tasks from conversation, checking sprint progress, or running te...
---
name: linear
description: "Manage Linear issues, projects, and cycles via GraphQL. Use when triaging backlogs, creating tasks from conversation, checking sprint progress, or running team reviews. Requires LINEAR_API_KEY."
metadata:
openclaw:
emoji: ๐ท
requires:
env:
- LINEAR_API_KEY
install:
- id: api-key
kind: manual
label: "Get your Linear API key"
steps:
- "Go to Linear โ Settings โ API โ Personal API keys"
- "Create a new key with read + write scope"
- "Set LINEAR_API_KEY=<your-key> in your OpenClaw workspace config"
---
## Purpose
Control Linear from OpenClaw. Create issues, triage backlogs, update priorities, and run sprint reviews โ all from your AI assistant, without switching tabs.
Works via Linear's GraphQL API using `curl`. No CLI or SDK required.
## When to Use
- Checking sprint progress mid-cycle
- Creating issues from meeting notes or conversation
- Triaging and reprioritizing a backlog
- Looking up an issue's status before a standup
- Bulk-updating labels, assignees, or priorities
- Generating a status report across projects or teams
## When NOT to Use
- Complex Figma/design reviews (use browser tooling)
- GitHub PR integration (use the `github` skill instead)
- Syncing Linear with Jira or other trackers (script with Linear's API directly)
## Setup
1. Open Linear โ Settings โ API โ Personal API keys
2. Create a key (read + write scope)
3. Add to your OpenClaw config:
```
LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxx
```
Verify the key works:
```bash
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { name } }"}' | jq .
```
You should see your name returned.
## Commands
### List your open issues
```bash
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "{ viewer { assignedIssues(filter: { state: { type: { nin: [\"completed\", \"cancelled\"] } } }) { nodes { id title priority state { name } team { name } } } } }"
}' | jq '.data.viewer.assignedIssues.nodes'
```
### Get issues for a team
```bash
TEAM_KEY="ENG" # your team's key
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"{ team(key: \\\"$TEAM_KEY\\\") { issues(filter: { state: { type: { eq: \\\"started\\\" } } }) { nodes { id identifier title assignee { name } priority } } } }\"
}" | jq '.data.team.issues.nodes'
```
### Create an issue
```bash
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation IssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title } } }",
"variables": {
"input": {
"teamId": "TEAM_ID_HERE",
"title": "Issue title from OpenClaw",
"description": "Created via OpenClaw linear skill.",
"priority": 2
}
}
}' | jq '.data.issueCreate'
```
Priority levels: `0` = No priority, `1` = Urgent, `2` = High, `3` = Medium, `4` = Low.
### Update an issue
```bash
ISSUE_ID="ISSUE_ID_HERE"
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"mutation { issueUpdate(id: \\\"$ISSUE_ID\\\", input: { priority: 1, stateId: \\\"STATE_ID\\\" }) { success issue { id title priority state { name } } } }\"
}" | jq '.data.issueUpdate'
```
### Search issues by keyword
```bash
QUERY="authentication bug"
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"{ issueSearch(query: \\\"$QUERY\\\") { nodes { id identifier title state { name } assignee { name } priority } } }\"
}" | jq '.data.issueSearch.nodes'
```
### Get current cycle (sprint) for a team
```bash
TEAM_KEY="ENG"
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"{ team(key: \\\"$TEAM_KEY\\\") { activeCycle { id name startsAt endsAt issues { nodes { id title state { name } estimate } } } } }\"
}" | jq '.data.team.activeCycle'
```
### Add a comment to an issue
```bash
ISSUE_ID="ISSUE_ID_HERE"
COMMENT="LGTM โ deploying to staging now."
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"mutation { commentCreate(input: { issueId: \\\"$ISSUE_ID\\\", body: \\\"$COMMENT\\\" }) { success comment { id body } } }\"
}" | jq '.data.commentCreate'
```
## Examples
**Morning standup prep:**
"What are my open Linear issues with priority Urgent or High?"
โ Run the "list your open issues" query, filter by priority โค 2.
**After a meeting:**
"Create a Linear issue in the ENG team: 'Fix auth token refresh race condition', High priority"
โ Look up ENG team ID, then run the create mutation with the title and priority=2.
**Sprint review:**
"Show me all completed issues in the current ENG cycle"
โ Get activeCycle, filter `state.type == "completed"`. Summarize with titles and estimates.
**Triage:**
"Move all issues assigned to @alice in ENG to 'In Progress'"
โ Fetch issues, get state ID for "In Progress", run issueUpdate for each.
## Notes
- **Rate limits**: Linear API allows 1,500 requests/hour. Cache results for bulk reads.
- **IDs vs keys**: Teams can be queried by `key` (e.g. `"ENG"`). Issues, states, and cycles require UUIDs. Get UUIDs from list queries.
- **GraphQL introspection**: Run `{ __schema { types { name } } }` to explore the full API schema.
- **Webhooks**: For real-time issue updates, configure a webhook in Linear โ Settings โ API โ Webhooks. Pair with the `webhook-listener` skill.
- **Pagination**: Large teams should use `first: N, after: cursor` pagination. Default page size is 50.
don't have the plugin yet? install it then click "run inline in claude" again.