Access the Linear API with managed OAuth authentication. Query and manage issues, projects, teams, cycles, labels, and comments using GraphQL.
CLI:
maton linear issue list -c ABC -L 10
maton api '/linear/graphql'
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': '{ viewer { id name email } }'}).encode()
req = urllib.request.Request('https://api.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://api.maton.ai/linear/graphql
All requests use POST to the GraphQL endpoint. Maton proxies requests to api.linear.app and automatically injects your OAuth token.
NPM:
npm install -g @maton-ai/cli
Homebrew:
brew install maton-ai/cli/maton
CLI:
maton login # Opens browser for API key
maton login --interactive # Skip browser, paste API key directly
maton whoami # Show current auth state
Manual:
MATON_API_KEY:export MATON_API_KEY="YOUR_API_KEY"
Manage your Linear OAuth connections at https://api.maton.ai.
CLI:
maton connection list linear --status ACTIVE
maton api -X GET /connections -f app=linear -f status=ACTIVE
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
CLI:
maton connection create linear
maton api /connections -f app=linear
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'linear'}).encode()
req = urllib.request.Request('https://api.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
CLI:
maton connection view {connection_id}
maton api /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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": "{connection_id}",
"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.
CLI:
maton connection delete {connection_id}
maton api -X DELETE /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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:
CLI:
maton linear issue list -c ABC --connection {connection_id}
maton api /linear/graphql --connection {connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'query': '{ viewer { id name } }'}).encode()
req = urllib.request.Request('https://api.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', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always specify the connection to ensure requests go to the intended account.
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 } }"}
Example:
maton linear whoami
POST /linear/graphql
Content-Type: application/json
{"query": "{ organization { id name urlKey } }"}
Example:
maton linear org view
POST /linear/graphql
Content-Type: application/json
{"query": "{ teams { nodes { id name key } } }"}
Example:
maton linear team list
POST /linear/graphql
Content-Type: application/json
{"query": "{ team(id: \"ABC\") { id name key issues { nodes { id identifier title } } } }"}
Example:
maton linear team view ABC
POST /linear/graphql
Content-Type: application/json
{"query": "{ issues(first: 10, filter: { team: { key: { eq: \"ABC\" } } }) { nodes { id identifier title state { name } priority createdAt } pageInfo { hasNextPage endCursor } } }"}
Example:
maton linear issue list -c ABC -L 10
POST /linear/graphql
Content-Type: application/json
{"query": "{ issue(id: \"ABC-123\") { id identifier title description state { name } priority assignee { name } team { key name } createdAt updatedAt } }"}
Example:
maton linear issue view ABC-123
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 } } } }"}
Example:
maton linear issue list --state started -L 10
Filter by title:
POST /linear/graphql
Content-Type: application/json
{"query": "{ issues(first: 10, filter: { title: { containsIgnoreCase: \"bug\" } }) { nodes { id identifier title } } }"}
Example:
maton linear issue list --title bug -L 10
POST /linear/graphql
Content-Type: application/json
{"query": "{ searchIssues(first: 10, term: \"shopify\") { nodes { id identifier title } } }"}
Example:
maton linear issue search shopify -L 10
POST /linear/graphql
Content-Type: application/json
{"query": "mutation { issueCreate(input: { teamId: \"TEAM_ID\", title: \"New issue title\" }) { success issue { id identifier title state { name } } } }"}
Example:
maton linear issue create --team-id TEAM_ID -t 'New issue title'
POST /linear/graphql
Content-Type: application/json
{"query": "mutation { issueUpdate(id: \"ABC-123\", input: { title: \"Updated title\", priority: 2 }) { success issue { id identifier title priority } } }"}
Example:
maton linear issue update ABC-123 -t 'Updated title' --priority 2
POST /linear/graphql
Content-Type: application/json
{"query": "{ projects(first: 10) { nodes { id name state createdAt } } }"}
Example:
maton linear project list
POST /linear/graphql
Content-Type: application/json
{"query": "{ cycles(first: 10) { nodes { id name number startsAt endsAt } } }"}
Example:
maton linear cycle list
POST /linear/graphql
Content-Type: application/json
{"query": "{ issueLabels(first: 20) { nodes { id name color } } }"}
Example:
maton linear label list
POST /linear/graphql
Content-Type: application/json
{"query": "{ workflowStates(first: 20) { nodes { id name type team { key } } } }"}
Example:
maton linear state list
POST /linear/graphql
Content-Type: application/json
{"query": "{ users(first: 20) { nodes { id name email active } } }"}
Example:
maton linear user list
POST /linear/graphql
Content-Type: application/json
{"query": "{ issue(id: \"ABC-123\") { comments(first: 10) { nodes { id body createdAt user { name } } } } }"}
Example:
maton linear comment list --issue ABC-123 -L 10
POST /linear/graphql
Content-Type: application/json
{"query": "mutation { commentCreate(input: { issueId: \"ABC-123\", body: \"Looking into this\" }) { success comment { id body } } }"}
Example:
maton linear comment create --issue ABC-123 -b 'Looking into this'
Linear uses Relay-style cursor-based pagination. The CLI automatically paginates with '--paginate'.
Example:
maton linear issue list -c ABC --paginate
# List issues for a team
maton linear issue list -c ABC -L 10
# View a specific issue
maton linear issue view ABC-123
# Create a new issue
maton linear issue create --team-id TEAM_ID -t 'Fix login'
# Add a comment
maton linear comment create --issue ABC-123 -b 'Looking into this'
const response = await fetch('https://api.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://api.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()
ABC-123 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
}
}
]
}
CLI:
maton whoami
maton connection list
Manual:
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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://api.maton.ai/linear/graphqlhttps://api.maton.ai/graphql共 3 个版本