Manage Linear issues, projects, and teams via the GraphQL API. Create, update, search, and organize issues. Uses API key auth (no OAuth needed). All operations via curl — no dependencies.
Manage Linear issues, projects, and teams via the GraphQL API. Create, update, search, and organize issues. Uses API key auth (no OAuth needed). All operations via curl — no dependencies.
Manage Linear issues, projects, and teams directly via the GraphQL API using curl. No MCP server, no OAuth flow, no extra dependencies.
Setup
Get a personal API key from Linear Settings > API > Personal API keys
Set LINEAR_API_KEY in your environment (via Moltis settings or your env config)
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 for issue(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.tool
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.
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.tool
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.tool
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.tool
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.tool
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.tool