Everything in this directory talks to the real Linear service.
- no mocks
- no fake transports
- no local stub server
Read these first if you have not created credentials yet:
Start by exporting either a real Linear personal API key:
export LINEAR_API_KEY=lin_api_...or a direct OAuth access token:
export LINEAR_OAUTH_ACCESS_TOKEN=...or by saving a token file first:
mix linear.oauth --save --manual --no-browserThis repo now has both:
- operator helpers such as
mix linear.oauthandexamples/run_all.sh --oauth - direct OAuth example scripts under
examples/oauth_*.exs
Do not go looking for an OAuth access token in Linear account preferences. The
OAuth access token is created by the OAuth code exchange itself. Use Linear's
app settings to get the client ID, client secret, and redirect URI, then let
mix linear.oauth print and optionally save the token for you.
The fastest onboarding path is now through examples/run_all.sh itself:
examples/run_all.sh --setup
examples/run_all.sh --setup-oauth
examples/run_all.sh --oauth
examples/run_all.shFor mutation examples:
examples/run_all.sh --setup-oauth-write
examples/run_all.sh --oauth-write
examples/run_all.sh --with-writeThose helpers are explicit shortcuts for the full commands:
examples/run_all.sh --oauth
# => mix linear.oauth --save --manual --no-browser
examples/run_all.sh --oauth-write
# => mix linear.oauth --save --manual --no-browser --scope read --scope write
examples/run_all.sh --oauth-refresh
# => mix linear.oauth refresh
examples/run_all.sh --oauth-client-credentials
# => mix linear.oauth client-credentials --save --scope read --scope write- Personal API key Created directly in Linear settings. Use it as-is against GraphQL.
- OAuth app
The app configuration in Linear. It gives you
client_id,client_secret, redirect URIs, and optional client-credentials support. It is not itself a bearer token. - OAuth access token The token returned by the app flow. This is what authenticates GraphQL.
- Refresh token Returned for authorization-code tokens. Use it to renew the access token.
If you want to prove that "the saved OAuth token is the credential being used",
run mix run examples/oauth_saved_token_viewer.exs.
These are the direct OAuth-focused examples, parallel to the style used in the Notion and GitHub repos:
mix run examples/oauth_authorize_url.exsBuilds a real Linear authorize URL and printsstateplus the generated PKCE verifier/challenge.mix run examples/oauth_exchange_code.exsExchanges a real Linear authorization code, then saves the token file.mix run examples/oauth_saved_token_viewer.exsUses the saved OAuth token file against the GraphQLviewerquery.mix run examples/oauth_refresh_and_viewer.exsRefreshes the saved token file, persists the new token, then callsviewer.mix run examples/oauth_application_info.exsCalls the generatedapplicationInfoGraphQL query for your OAuth app's client ID.
Set the minimum app values:
export LINEAR_OAUTH_CLIENT_ID="..."
export LINEAR_OAUTH_CLIENT_SECRET="..."
export LINEAR_OAUTH_REDIRECT_URI="http://127.0.0.1:40071/callback"If you are trying to find the app setup page inside Linear, the current
official docs point to Settings -> API -> Your Applications for OAuth apps.
That is different from account preferences.
Then either run the helper:
examples/run_all.sh --oauthwhich expands to:
mix linear.oauth --save --manual --no-browserThat flow prints the authorize URL, asks you to approve the app in Linear, then exchanges the returned code and saves the token file at:
~/.config/linear_sdk/oauth/linear.json
unless you override LINEAR_OAUTH_TOKEN_PATH.
If you want a write-capable token for the mutation examples, request explicit write scope:
examples/run_all.sh --oauth-write
# expands to:
mix linear.oauth --save --manual --no-browser --scope read --scope writeIf the optional callback-listener dependencies are available and you have a literal loopback redirect URI, you can let the task capture the callback directly:
mix linear.oauth --savemix linear.oauth refreshIf your Linear app is configured for client credentials:
mix linear.oauth client-credentials --save --scope read --scope writeFastest first run:
mix run examples/viewer.exs
examples/run_all.shRun the full read-only suite:
examples/run_all.shThat script accepts LINEAR_API_KEY, LINEAR_OAUTH_ACCESS_TOKEN, or a saved
LINEAR_OAUTH_TOKEN_PATH file. It auto-discovers a project slug and issue when
those values are not set. If your workspace has no accessible project slug yet,
the candidate issue example falls back to a workspace-scoped query so the
read-only suite still runs.
If you also want the write examples:
examples/run_all.sh --with-writeIf you want setup instructions without running anything:
examples/run_all.sh --setup
examples/run_all.sh --setup-api-key
examples/run_all.sh --setup-oauth
examples/run_all.sh --setup-oauth-write
examples/run_all.sh --setup-client-credentialsOAuth application model and saved-token examples:
mix run examples/oauth_authorize_url.exs
mix run examples/oauth_exchange_code.exs
mix run examples/oauth_saved_token_viewer.exs
mix run examples/oauth_refresh_and_viewer.exs
mix run examples/oauth_application_info.exsCurrent user:
mix run examples/viewer.exsSymphony-style candidate issue polling:
mix run examples/symphony_candidate_issues.exsSymphony-style candidate issue polling for the current viewer:
mix run examples/symphony_candidate_issues_me.exsOptional project scoping and active-state override:
export LINEAR_PROJECT_SLUG=customer-portal-4f2a8c1d9e6b
export LINEAR_ACTIVE_STATES="Todo,In Progress"
mix run examples/symphony_candidate_issues.exsOptional assignee routing, matching Symphony's "me" behavior:
export LINEAR_ASSIGNEE=me
mix run examples/symphony_candidate_issues.exsResolve an issue reference and a target workflow state:
mix run examples/symphony_state_lookup.exsFetch issue state snapshots by internal Linear issue IDs, matching Symphony's
issues(filter: {id: {in: ...}}) path:
mix run examples/symphony_issue_states_by_ids.exsResolve a workflow state ID using Symphony's exact filtered team.states(...)
lookup query:
mix run examples/symphony_state_id_lookup.exsOptional explicit issue and target state:
export LINEAR_ISSUE_REF=ENG-123
export LINEAR_TARGET_STATE="In Progress"
mix run examples/symphony_state_lookup.exsThese perform real mutations. Use a disposable issue and require explicit confirmation:
examples/run_all.sh --with-writeLow-level equivalent:
export LINEAR_CONFIRM_WRITE=1Create a comment:
export LINEAR_ISSUE_REF=ENG-123
export LINEAR_COMMENT_BODY="Live test comment from LinearSDK examples"
mix run examples/symphony_comment.exsTransition an issue to another workflow state:
export LINEAR_ISSUE_REF=ENG-123
mix run examples/symphony_transition_issue.exsOptional explicit target state:
export LINEAR_ISSUE_REF=ENG-123
export LINEAR_TARGET_STATE="In Progress"
mix run examples/symphony_transition_issue.exsLINEAR_API_KEY- optional when you prefer a personal API key
LINEAR_OAUTH_CLIENT_ID- required for the direct OAuth example scripts and
mix linear.oauth
- required for the direct OAuth example scripts and
LINEAR_OAUTH_CLIENT_SECRET- required for confidential-client flows; optional for PKCE exchange and refresh when Linear allows it
LINEAR_OAUTH_REDIRECT_URI- required for direct OAuth example scripts and
mix linear.oauth
- required for direct OAuth example scripts and
LINEAR_OAUTH_AUTH_CODE- optional; when unset,
examples/oauth_exchange_code.exsprompts for the full redirect URL or raw authorization code
- optional; when unset,
LINEAR_OAUTH_PKCE_VERIFIER- optional unless you are exchanging a code from a PKCE authorization request
such as
examples/oauth_authorize_url.exs
- optional unless you are exchanging a code from a PKCE authorization request
such as
LINEAR_OAUTH_SCOPES- optional scopes for
examples/oauth_authorize_url.exs, accepts comma-separated or space-separated values
- optional scopes for
LINEAR_OAUTH_ACTOR- optional actor override for
examples/oauth_authorize_url.exs, useuserorapp
- optional actor override for
LINEAR_OAUTH_ACCESS_TOKEN- optional when you already have an OAuth token
LINEAR_OAUTH_TOKEN_PATH- optional path to a saved token file, defaults to
~/.config/linear_sdk/oauth/linear.json
- optional path to a saved token file, defaults to
LINEAR_PROJECT_SLUG- optional for examples; auto-discovered when omitted. Symphony's own tracker config still requires a project slug
LINEAR_ACTIVE_STATES- optional comma-separated state names, defaults to
Todo,In Progress
- optional comma-separated state names, defaults to
LINEAR_ASSIGNEE- optional for candidate polling; use
meor a Linear user ID. The dedicatedsymphony_candidate_issues_me.exsexample needs no extra env
- optional for candidate polling; use
LINEAR_ISSUE_REF- optional; auto-discovered when omitted. You can still set it explicitly to
use an issue identifier like
ENG-123or a Linear issue UUID
- optional; auto-discovered when omitted. You can still set it explicitly to
use an issue identifier like
LINEAR_TARGET_STATE- optional; state lookup defaults to the issue's current state, while the transition example auto-picks a different workflow state when possible
LINEAR_COMMENT_BODY- optional; defaults to a canned live test comment
LINEAR_CONFIRM_WRITE- optional low-level equivalent of
examples/run_all.sh --with-write
- optional low-level equivalent of
examples/run_all.sh- runs the read-only suite with only
LINEAR_API_KEY; add--with-writeto include the write examples
- runs the read-only suite with only
mix run examples/oauth_authorize_url.exsmix run examples/oauth_exchange_code.exsmix run examples/oauth_saved_token_viewer.exsmix run examples/oauth_application_info.exsmix run examples/viewer.exsmix run examples/symphony_candidate_issues.exsmix run examples/symphony_candidate_issues_me.exsmix run examples/symphony_issue_states_by_ids.exsmix run examples/symphony_state_id_lookup.exsmix run examples/symphony_state_lookup.exsmix run examples/oauth_refresh_and_viewer.exsmix run examples/symphony_comment.exsmix run examples/symphony_transition_issue.exs
If you still need the onboarding steps inside Linear itself, read
guides/real-linear-usage.md.