Comprehensive, practical reference for Linear's GraphQL API and how linctl maps onto it.
Last verified against official docs: March 1, 2026.
- Developers hub: https://linear.app/developers
- GraphQL endpoint:
https://api.linear.app/graphql - Schema explorer: https://studio.apollographql.com/public/Linear-API/variant/current/explorer
- Changelog/deprecations: https://linear.app/changelog and https://linear.app/developers/deprecations
- Header format:
Authorization: <API_KEY> - Good fit for local automation and operators.
- Header format:
Authorization: Bearer <ACCESS_TOKEN> - Supported grants include user authorization flow and app-actor flow (
actor=app). - Modern app capabilities include app actor tokens and app scopes.
Common scopes (workspace-specific):
readwriteissues:createcomments:createtimeSchedule:writeadminapp:assignableapp:mentionable
Notes:
- Older scope lists like
issues:write/teams:readare stale. - For older apps, follow OAuth migration guidance (refresh-token / actor updates) in the official docs.
https://api.linear.app/graphql
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
types { name }
}
}- Most node lookups accept a Linear ID (
id). - Some API paths use domain keys/identifiers (for example team key like
ENG, issue identifier likeENG-123) after resolving via query logic. - In
linctl, user-facing commands often accept keys/identifiers and resolve IDs internally.
- Connections use cursor pagination.
- Standard args:
first,after,last,before. - Standard response:
nodes+pageInfo { hasNextPage endCursor ... }.
- Filtering is typed per resource (
IssueFilter,ProjectFilter, etc.). - Sorting is typed per resource (
IssueOrderBy,ProjectOrderBy,TeamOrderBy,UserOrderBy,CommentOrderBy). - Do not assume global
PaginationOrderByfor every query.
Linear enforces both request-count and complexity budgets.
- API key: 5,000 requests/hour
- OAuth key: 5,000 requests/hour
- Unauthenticated: 60 requests/hour
- Endpoint burst protection: 30 requests/minute per endpoint
- Max single request complexity: 10,000
- API key hourly complexity budget: 3,000,000
- OAuth hourly complexity budget: 2,000,000
- Unauthenticated hourly complexity budget: 10,000
Always trust live response headers for enforcement details in your workspace.
Request-based:
X-RateLimit-Requests-LimitX-RateLimit-Requests-RemainingX-RateLimit-Requests-Reset
Complexity-based:
X-RateLimit-Complexity-LimitX-RateLimit-Complexity-RemainingX-RateLimit-Complexity-Reset
Endpoint-specific (when applicable):
X-RateLimit-Endpoint-Requests-LimitX-RateLimit-Endpoint-Requests-RemainingX-RateLimit-Endpoint-Requests-Reset
Linear's GraphQL schema is broad and evolving. This section lists the practical domains you should expect to query/mutate.
- List/search/get issues
- Create/update issues
- Assign/delegate, set labels, parent/sub-issue links
- State transitions (workflow state)
- Attachments, comments, SLA/triage related metadata
Representative examples:
query Issues($filter: IssueFilter, $orderBy: IssueOrderBy, $first: Int, $after: String) {
issues(filter: $filter, orderBy: $orderBy, first: $first, after: $after) {
nodes {
id
identifier
title
priority
state { id name type color }
team { id key name }
assignee { id name email }
createdAt
updatedAt
}
pageInfo { hasNextPage endCursor }
}
}mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) {
issueUpdate(id: $id, input: $input) {
success
issue { id identifier title }
}
}IssueRelationTypeenum:blocks,duplicate,related,similar. (blocked_byis not an API type — it is a CLI convenience that maps toblockswith the two issue IDs swapped.)- Relations are directional, and the direction is the field most often read
backwards. Per the schema descriptions (verify via introspection, see §2):
IssueRelation.issue— "The source issue whose relationship is being described. This is the issue from which the relation originates."IssueRelation.relatedIssue— "The target issue that the source issue is related to. The relation type describes how the source issue relates to this issue."
- So for
type: "blocks",issueblocksrelatedIssue—issueis the blocker,relatedIssueis the one that must wait. The same source-acts-on- target convention applies toduplicate(issueis a duplicate ofrelatedIssue). - An issue's
relationsconnection holds edges where it is the source;inverseRelationsholds edges where it is the target. To render a direction- aware label you need both, plus a flag recording which connection each edge came from — the counterpart isrelatedIssuefor forward edges andissuefor inverse ones. IssueRelationCreateInputtakesissueId,relatedIssueId,type. Both ID fields accept a UUID or an issue identifier (e.g.LIN-123).
mutation IssueRelationCreate($input: IssueRelationCreateInput!) {
issueRelationCreate(input: $input) {
success
issueRelation {
id
type
issue { id identifier }
relatedIssue { id identifier }
}
}
}- List/get/create/update/archive/delete projects
- Manage lead, state, target dates, teams
- Query milestones and link issues to milestones
query Projects($filter: ProjectFilter, $orderBy: ProjectOrderBy, $first: Int, $after: String) {
projects(filter: $filter, orderBy: $orderBy, first: $first, after: $after) {
nodes {
id
name
state
progress
startDate
targetDate
lead { id name email }
teams { nodes { id key name } }
}
pageInfo { hasNextPage endCursor }
}
}- Roadmap entities exist beyond projects (initiatives and updates).
- Use schema explorer for exact query/mutation names and current fields in your workspace tier.
- Customer and customer-request objects are first-class API surfaces.
- Webhooks include customer/customerRequest events.
- List/get teams
- List team members
- List/update team workflow statuses
query TeamStates($key: String!) {
team(id: $key) {
states {
nodes {
id
name
type
color
description
position
}
}
}
}mutation WorkflowStateUpdate($id: String!, $input: WorkflowStateUpdateInput!) {
workflowStateUpdate(id: $id, input: $input) {
success
workflowState { id name type color description position }
}
}- Viewer/current-user query
- Workspace user listing and lookup
- Admin/active/guest metadata
- Team label listing
- Create/update/delete labels
- Parent/group label relationships
- List by issue
- Create/get/update/delete comment
- Create URL/PR attachments
- Rich metadata supported via attachment input payloads
- Agent session/delegation state is queryable from issue context.
- Mentioning an agent from issue context is supported (with app scopes for agent actors where applicable).
- Manage webhook endpoints via API.
- Event coverage includes classic issue/project/comment events plus modern domains (initiative, customer, customerRequest, issue SLA, and more).
query Webhooks($first: Int, $after: String) {
webhooks(first: $first, after: $after) {
nodes {
id
url
label
enabled
resourceTypes
}
pageInfo { hasNextPage endCursor }
}
}- Verify signatures and reject unsigned payloads.
- Design handlers to be idempotent.
- Expect retries and out-of-order arrivals.
- Persist webhook event IDs to suppress duplicates.
{
team: { id: { eq: "TEAM_ID" } }
assignee: { id: { eq: "USER_ID" } }
state: { name: { eq: "In Progress" } }
priority: { eq: 1 }
createdAt: { gte: "2026-01-01T00:00:00Z" }
}eq,neqin,nincontains,containsIgnoreCasestartsWith,endsWith- date/time comparators such as
gte,lte
Always validate exact comparator support per field/type in the schema explorer.
linctl covers a focused subset of the full GraphQL surface.
linctl auth
linctl auth login
linctl auth status
linctl auth logout
linctl whoamilinctl graphql [query]
linctl graphql --query 'query { viewer { id } }'
linctl graphql --file query.graphql --variables-file vars.json
cat query.graphql | linctl graphql --variables '{"k":"ENG"}'linctl issue list|ls
linctl issue search|find
linctl issue get|show
linctl issue create|new
linctl issue update
linctl issue assign
linctl issue attach
linctl issue attachment list|ls
linctl issue attachment download|dllinctl project list|ls
linctl project get|show
linctl project create|new
linctl project update
linctl project delete|rm|removelinctl team list|ls
linctl team get|show
linctl team members
linctl team statuses|states
linctl team status-update|state-updatelinctl user list|ls
linctl user get|show
linctl user melinctl label list|ls
linctl label get
linctl label create
linctl label update
linctl label delete|rm|removelinctl comment list|ls
linctl comment create|add|new
linctl comment get|show
linctl comment update|edit
linctl comment delete|rm|removelinctl agent <issue-id>
linctl agent mention <issue-id> [message...]--json, -j--plaintext, -p--config--help, -h--version, -v
Not all modern Linear API domains are exposed in linctl commands yet (for example deeper roadmap/customer/admin/app management surfaces). For unsupported flows:
- Query schema explorer for exact operation and input names.
- Execute the operation via
linctl graphqlso auth/config/output stay in one tool. - Add CLI coverage only when workflow value is clear.
Before shipping CLI/API docs changes:
- Validate claims against official docs and schema explorer.
- Run
go test ./.... - For command changes, update
README.md,SKILL.md, and this file in the same PR. - Avoid hardcoding stale limits/scopes unless source-linked.
- https://linear.app/developers/graphql
- https://linear.app/developers/oauth-2-0-authentication
- https://linear.app/developers/oauth-actor-authorization
- https://linear.app/developers/rate-limiting
- https://linear.app/developers/filtering
- https://linear.app/developers/pagination
- https://linear.app/developers/webhooks
- https://linear.app/developers/attachments
- https://linear.app/developers/agents
- https://linear.app/developers/managing-customers
- https://linear.app/developers/deprecations
- https://linear.app/changelog