linear-apiLinear 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.
Install via ClawdBot CLI:
clawdbot install byungkyu/linear-apiAccess 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"
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 } }"}
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"
}
]
}
}
}
POST /linear/graphql
Content-Type: application/json
{"query": "{ team(id: \"TEAM_ID\") { id name key issues { nodes { id identifier title } } } }"}
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"
}
}
}
}
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 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 } } }"}
POST /linear/graphql
Content-Type: application/json
{"query": "{ searchIssues(first: 10, term: \"shopify\") { nodes { id identifier title } } }"}
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" }
}
}
}
}
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 } } }"}
POST /linear/graphql
Content-Type: application/json
{"query": "{ projects(first: 10) { nodes { id name state createdAt } } }"}
POST /linear/graphql
Content-Type: application/json
{"query": "{ cycles(first: 10) { nodes { id name number startsAt endsAt } } }"}
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 } } }"}
POST /linear/graphql
Content-Type: application/json
{"query": "{ comments(first: 10) { nodes { id body createdAt issue { identifier } user { name } } } }"}
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()
MTN-527 can be used in place of UUIDs for the id parameterbacklog, unstarted, started, completed, canceledhttps://api.linear.app/graphqlsearchIssues(term: "...") for full-text search across issues| Status | Meaning |
|--------|---------|
| 400 | Missing Linear connection or GraphQL validation error |
| 401 | Invalid or missing Maton API key |
| 403 | Insufficient OAuth scope for the operation |
| 429 | Rate limited |
| 4xx/5xx | Passthrough 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
}
}
]
}
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
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
linear. For example:https://gateway.maton.ai/linear/graphqlhttps://gateway.maton.ai/graphqlAI Usage Analysis
Analysis is being generated⦠refresh in a few seconds.
Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.
Connect to 100+ APIs (Google Workspace, Microsoft 365, GitHub, Notion, Slack, Airtable, HubSpot, etc.) with managed OAuth. Use this skill when users want to...
Build, debug, and deploy websites using HTML, CSS, JavaScript, and modern frameworks following production best practices.
YouTube Data API integration with managed OAuth. Search videos, manage playlists, access channel data, and interact with comments. Use this skill when users want to interact with YouTube. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
Scaffold, test, document, and debug REST and GraphQL APIs. Use when the user needs to create API endpoints, write integration tests, generate OpenAPI specs, test with curl, mock APIs, or troubleshoot HTTP issues.
Search for jobs across LinkedIn, Indeed, Glassdoor, ZipRecruiter, Google Jobs, Bayt, Naukri, and BDJobs using the JobSpy MCP server.