Linear — Linear: manage issues, projects, teams via GraphQL + curl
Linear
Section titled “Linear”Linear: manage issues, projects, teams via GraphQL + curl.
Skill metadata
Section titled “Skill metadata”| Source | Bundled (installed by default) |
| Path | skills/productivity/linear |
| Version | 1.0.0 |
| Author | Hermes Agent |
| License | MIT |
| Tags | Linear, Project Management, Issues, GraphQL, API, Productivity |
Reference: full SKILL.md
Section titled “Reference: full SKILL.md”Info The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
Linear — Issue & Project Management
Section titled “Linear — Issue & Project Management”Manage Linear issues, projects, and teams directly via the GraphQL API using curl. No MCP server, no OAuth flow, no extra dependencies.
- Get a personal API key from Linear Settings > API > Personal API keys
- Set
LINEAR_API_KEYin your environment (viahermes setupor your env config)
API Basics
Section titled “API Basics”- Endpoint:
https://api.linear.app/graphql(POST) - Auth header:
Authorization: $LINEAR_API_KEY(no “Bearer” prefix for API keys) - All requests are POST with
Content-Type: application/json - Both UUIDs and short identifiers (e.g.,
ENG-123) work forissue(id:)
Base curl pattern:
curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ viewer { id name } }"}' | python3 -m json.toolWorkflow States
Section titled “Workflow States”Linear uses WorkflowState objects with a type field. 6 state types:
| Type | Description |
|---|---|
triage | Incoming issues needing review |
backlog | Acknowledged but not yet planned |
unstarted | Planned/ready but not started |
started | Actively being worked on |
completed | Done |
canceled | Won’t do |
Each team has its own named states (e.g., “In Progress” is type started). To change an issue’s status, you need the stateId (UUID) of the target state — query workflow states first.
Priority values: 0 = None, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low
Common Queries
Section titled “Common Queries”Get current user
Section titled “Get current user”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ viewer { id name email } }"}' | python3 -m json.toolList teams
Section titled “List teams”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ teams { nodes { id name key } } }"}' | python3 -m json.toolList workflow states for a team
Section titled “List workflow states for a team”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ workflowStates(filter: { team: { key: { eq: \"ENG\" } } }) { nodes { id name type } } }"}' | python3 -m json.toolList issues (first 20)
Section titled “List issues (first 20)”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issues(first: 20) { nodes { identifier title priority state { name type } assignee { name } team { key } url } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.toolList my assigned issues
Section titled “List my assigned issues”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ viewer { assignedIssues(first: 25) { nodes { identifier title state { name type } priority url } } } }"}' | python3 -m json.toolGet a single issue (by identifier like ENG-123)
Section titled “Get a single issue (by identifier like ENG-123)”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issue(id: \"ENG-123\") { id identifier title description priority state { id name type } assignee { id name } team { key } project { name } labels { nodes { name } } comments { nodes { body user { name } createdAt } } url } }"}' | python3 -m json.toolSearch issues by text
Section titled “Search issues by text”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issueSearch(query: \"bug login\", first: 10) { nodes { identifier title state { name } assignee { name } url } } }"}' | python3 -m json.toolFilter issues by state type
Section titled “Filter issues by state type”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issues(filter: { state: { type: { in: [\"started\"] } } }, first: 20) { nodes { identifier title state { name } assignee { name } } } }"}' | python3 -m json.toolFilter by team and assignee
Section titled “Filter by team and assignee”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issues(filter: { team: { key: { eq: \"ENG\" } }, assignee: { email: { eq: \"[email protected]\" } } }, first: 20) { nodes { identifier title state { name } priority } } }"}' | python3 -m json.toolList projects
Section titled “List projects”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ projects(first: 20) { nodes { id name description progress lead { name } teams { nodes { key } } url } } }"}' | python3 -m json.toolList team members
Section titled “List team members”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ users { nodes { id name email active } } }"}' | python3 -m json.toolList labels
Section titled “List labels”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issueLabels { nodes { id name color } } }"}' | python3 -m json.toolCommon Mutations
Section titled “Common Mutations”Create an issue
Section titled “Create an issue”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }", "variables": { "input": { "teamId": "TEAM_UUID", "title": "Fix login bug", "description": "Users cannot login with SSO", "priority": 2 } } }' | python3 -m json.toolUpdate issue status
Section titled “Update issue status”First get the target state UUID from the workflow states query above, then:
curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { stateId: \"STATE_UUID\" }) { success issue { identifier state { name type } } } }"}' | python3 -m json.toolAssign an issue
Section titled “Assign an issue”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { assigneeId: \"USER_UUID\" }) { success issue { identifier assignee { name } } } }"}' | python3 -m json.toolSet priority
Section titled “Set priority”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { priority: 1 }) { success issue { identifier priority } } }"}' | python3 -m json.toolAdd a comment
Section titled “Add a comment”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_UUID\", body: \"Investigated. Root cause is X.\" }) { success comment { id body } } }"}' | python3 -m json.toolSet due date
Section titled “Set due date”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { dueDate: \"2026-04-01\" }) { success issue { identifier dueDate } } }"}' | python3 -m json.toolAdd labels to an issue
Section titled “Add labels to an issue”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { labelIds: [\"LABEL_UUID_1\", \"LABEL_UUID_2\"] }) { success issue { identifier labels { nodes { name } } } } }"}' | python3 -m json.toolAdd issue to a project
Section titled “Add issue to a project”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { projectId: \"PROJECT_UUID\" }) { success issue { identifier project { name } } } }"}' | python3 -m json.toolCreate a project
Section titled “Create a project”curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id name url } } }", "variables": { "input": { "name": "Q2 Auth Overhaul", "description": "Replace legacy auth with OAuth2 and PKCE", "teamIds": ["TEAM_UUID"] } } }' | python3 -m json.toolPagination
Section titled “Pagination”Linear uses Relay-style cursor pagination:
# First pagecurl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issues(first: 20) { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.tool
# Next page — use endCursor from previous responsecurl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ issues(first: 20, after: \"CURSOR_FROM_PREVIOUS\") { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.toolDefault page size: 50. Max: 250. Always use first: N to limit results.
Filtering Reference
Section titled “Filtering Reference”Comparators: eq, neq, in, nin, lt, lte, gt, gte, contains, startsWith, containsIgnoreCase
Combine filters with or: [...] for OR logic (default is AND within a filter object).
Typical Workflow
Section titled “Typical Workflow”- Query teams to get team IDs and keys
- Query workflow states for target team to get state UUIDs
- List or search issues to find what needs work
- Create issues with team ID, title, description, priority
- Update status by setting
stateIdto the target workflow state - Add comments to track progress
- Mark complete by setting
stateIdto the team’s “completed” type state
Rate Limits
Section titled “Rate Limits”- 5,000 requests/hour per API key
- 3,000,000 complexity points/hour
- Use
first: Nto limit results and reduce complexity cost - Monitor
X-RateLimit-Requests-Remainingresponse header
Important Notes
Section titled “Important Notes”- Always use
terminaltool withcurlfor API calls — do NOT useweb_extractorbrowser - Always check the
errorsarray in GraphQL responses — HTTP 200 can still contain errors - If
stateIdis omitted when creating issues, Linear defaults to the first backlog state - The
descriptionfield supports Markdown - Use
python3 -m json.toolorjqto format JSON responses for readability