Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Linear API integration with managed OAuth. Query and manage issues, projects, teams, cycles, and labels using GraphQL. Use this skill when users want to create, update, or query Linear issues, search for tasks, manage projects, or track work. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
Linear API integration with managed OAuth. Query and manage issues, projects, teams, cycles, and labels using GraphQL. Use this skill when users want to create, update, or query Linear issues, search for tasks, manage projects, or track work. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Access the Linear API with managed OAuth authentication. Query and manage issues, projects, teams, cycles, labels, and comments using GraphQL.
# Get current user python <<'EOF' import urllib.request, os, json data = json.dumps({'query': '{ viewer { id name email } }'}).encode() req = urllib.request.Request('https://gateway.maton.ai/linear/graphql', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
https://gateway.maton.ai/linear/graphql All requests use POST to the GraphQL endpoint. The gateway proxies requests to api.linear.app and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header: Authorization: Bearer $MATON_API_KEY Environment Variable: Set your API key as MATON_API_KEY: export MATON_API_KEY="YOUR_API_KEY"
Sign in or create an account at maton.ai Go to maton.ai/settings Copy your API key
Manage your Linear OAuth connections at https://ctrl.maton.ai.
python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=linear&status=ACTIVE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
python <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'linear'}).encode() req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF Response: { "connection": { "connection_id": "fda4dabb-9d62-47e3-9503-a2f29d0995df", "status": "ACTIVE", "creation_time": "2026-02-04T23:03:22.676001Z", "last_updated_time": "2026-02-04T23:03:51.239577Z", "url": "https://connect.maton.ai/?session_token=...", "app": "linear", "metadata": {} } } Open the returned url in a browser to complete OAuth authorization.
python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
If you have multiple Linear connections, specify which one to use with the Maton-Connection header: python <<'EOF' import urllib.request, os, json data = json.dumps({'query': '{ viewer { id name } }'}).encode() req = urllib.request.Request('https://gateway.maton.ai/linear/graphql', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') req.add_header('Maton-Connection', 'fda4dabb-9d62-47e3-9503-a2f29d0995df') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF If omitted, the gateway uses the default (oldest) active connection.
Linear uses a GraphQL API. All operations are sent as POST requests with a JSON body containing the query field.
POST /linear/graphql Content-Type: application/json {"query": "{ viewer { id name email } }"} Response: { "data": { "viewer": { "id": "4933b394-c42f-4623-904f-355fc40a4858", "name": "Byungkyu Park", "email": "byungkyujpark@gmail.com" } } }
POST /linear/graphql Content-Type: application/json {"query": "{ organization { id name urlKey } }"}
List Teams POST /linear/graphql Content-Type: application/json {"query": "{ teams { nodes { id name key } } }"} Response: { "data": { "teams": { "nodes": [ { "id": "70c49a0d-6973-4563-a743-8504f1a5171b", "name": "Maton", "key": "MTN" } ] } } } Get Team POST /linear/graphql Content-Type: application/json {"query": "{ team(id: \"TEAM_ID\") { id name key issues { nodes { id identifier title } } } }"}
List Issues POST /linear/graphql Content-Type: application/json {"query": "{ issues(first: 10) { nodes { id identifier title state { name } priority createdAt } pageInfo { hasNextPage endCursor } } }"} Response: { "data": { "issues": { "nodes": [ { "id": "565e2ee9-2552-48d8-bbf9-a8b79ca1baec", "identifier": "MTN-527", "title": "Shopify app verification", "state": { "name": "In Progress" }, "priority": 0, "createdAt": "2026-02-03T07:49:31.675Z" } ], "pageInfo": { "hasNextPage": true, "endCursor": "4c7b33c8-dabf-47ce-9d30-7f286f9463be" } } } } Get Issue by ID or Identifier POST /linear/graphql Content-Type: application/json {"query": "{ issue(id: \"MTN-527\") { id identifier title description state { name } priority assignee { name } team { key name } createdAt updatedAt } }"} Filter Issues Filter by state type: POST /linear/graphql Content-Type: application/json {"query": "{ issues(first: 10, filter: { state: { type: { eq: \"started\" } } }) { nodes { id identifier title state { name type } } } }"} Filter by title: POST /linear/graphql Content-Type: application/json {"query": "{ issues(first: 10, filter: { title: { containsIgnoreCase: \"bug\" } }) { nodes { id identifier title } } }"} Search Issues POST /linear/graphql Content-Type: application/json {"query": "{ searchIssues(first: 10, term: \"shopify\") { nodes { id identifier title } } }"} Create Issue POST /linear/graphql Content-Type: application/json {"query": "mutation { issueCreate(input: { teamId: \"TEAM_ID\", title: \"New issue title\", description: \"Issue description\" }) { success issue { id identifier title state { name } } } }"} Response: { "data": { "issueCreate": { "success": true, "issue": { "id": "9dff693f-27d2-4656-9b2d-baa4a828dc83", "identifier": "MTN-528", "title": "New issue title", "state": { "name": "Backlog" } } } } } Update Issue POST /linear/graphql Content-Type: application/json {"query": "mutation { issueUpdate(id: \"ISSUE_ID\", input: { title: \"Updated title\", priority: 2 }) { success issue { id identifier title priority } } }"}
List Projects POST /linear/graphql Content-Type: application/json {"query": "{ projects(first: 10) { nodes { id name state createdAt } } }"}
List Cycles POST /linear/graphql Content-Type: application/json {"query": "{ cycles(first: 10) { nodes { id name number startsAt endsAt } } }"}
List Labels POST /linear/graphql Content-Type: application/json {"query": "{ issueLabels(first: 20) { nodes { id name color } } }"} Response: { "data": { "issueLabels": { "nodes": [ { "id": "510edbdf-9f6e-43a0-80e5-c3b3bd82e26f", "name": "Blocked", "color": "#eb5757" }, { "id": "cb7a7ef2-d2d3-4da2-ad4e-7cea0f8a72c7", "name": "Feature", "color": "#BB87FC" }, { "id": "c795d04c-24d2-4d20-b3c1-9f9f1ce7b017", "name": "Improvement", "color": "#4EA7FC" }, { "id": "40ff69f9-4a93-40a2-b143-f3b94aa594b7", "name": "Bug", "color": "#EB5757" } ] } } }
POST /linear/graphql Content-Type: application/json {"query": "{ workflowStates(first: 20) { nodes { id name type team { key } } } }"} Response: { "data": { "workflowStates": { "nodes": [ { "id": "f21dfa65-7951-4742-a202-00ceb0ff6e9f", "name": "Backlog", "type": "backlog", "team": { "key": "MTN" } }, { "id": "1ab9475f-eb91-4207-a5a3-1176e38b85be", "name": "Todo", "type": "unstarted", "team": { "key": "MTN" } }, { "id": "ee724a62-0212-4b53-af67-08297a5ae132", "name": "In Progress", "type": "started", "team": { "key": "MTN" } }, { "id": "427a9916-3849-4303-b982-f00f1d79c5ee", "name": "Done", "type": "completed", "team": { "key": "MTN" } }, { "id": "363df32a-f22d-4083-8efb-b3615c019925", "name": "Canceled", "type": "canceled", "team": { "key": "MTN" } } ] } } }
POST /linear/graphql Content-Type: application/json {"query": "{ users(first: 20) { nodes { id name email active } } }"}
List Comments POST /linear/graphql Content-Type: application/json {"query": "{ comments(first: 10) { nodes { id body createdAt issue { identifier } user { name } } } }"} Create Comment POST /linear/graphql Content-Type: application/json {"query": "mutation { commentCreate(input: { issueId: \"ISSUE_ID\", body: \"Comment text here\" }) { success comment { id body } } }"}
Linear uses Relay-style cursor-based pagination with first/after and last/before arguments. # First page POST /linear/graphql {"query": "{ issues(first: 10) { nodes { id identifier title } pageInfo { hasNextPage endCursor } } }"} # Next page using endCursor POST /linear/graphql {"query": "{ issues(first: 10, after: \"CURSOR_VALUE\") { nodes { id identifier title } pageInfo { hasNextPage endCursor } } }"} Response includes pageInfo: { "data": { "issues": { "nodes": [...], "pageInfo": { "hasNextPage": true, "endCursor": "4c7b33c8-dabf-47ce-9d30-7f286f9463be" } } } }
const response = await fetch('https://gateway.maton.ai/linear/graphql', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `{ issues(first: 10) { nodes { id identifier title state { name } } } }` }) }); const data = await response.json();
import os import requests response = requests.post( 'https://gateway.maton.ai/linear/graphql', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'Content-Type': 'application/json' }, json={ 'query': '{ issues(first: 10) { nodes { id identifier title state { name } } } }' } ) data = response.json()
Linear uses GraphQL exclusively (no REST API) Issue identifiers like MTN-527 can be used in place of UUIDs for the id parameter Priority values: 0 = No priority, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low Workflow state types: backlog, unstarted, started, completed, canceled The GraphQL schema is introspectable at https://api.linear.app/graphql Use searchIssues(term: "...") for full-text search across issues Some mutations (delete, create labels/projects) may require additional OAuth scopes. If you receive a scope error, contact Maton support at support@maton.ai with the specific operations/APIs you need and your use-case
StatusMeaning400Missing Linear connection or GraphQL validation error401Invalid or missing Maton API key403Insufficient OAuth scope for the operation429Rate limited4xx/5xxPassthrough error from Linear API GraphQL errors are returned in the errors array: { "errors": [ { "message": "Invalid scope: `write` required", "extensions": { "type": "forbidden", "code": "FORBIDDEN", "statusCode": 403 } } ] }
Check that the MATON_API_KEY environment variable is set: echo $MATON_API_KEY Verify the API key is valid by listing connections: python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
Ensure your URL path starts with linear. For example: Correct: https://gateway.maton.ai/linear/graphql Incorrect: https://gateway.maton.ai/graphql
Linear API Overview Linear GraphQL Getting Started Linear GraphQL Schema (Apollo Studio) Linear API and Webhooks Maton Community Maton Support
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.