Oxagen Docs

Linear

Sync Linear issues, projects, milestones, initiatives, customers, and status updates into the workspace knowledge graph. The connector cross-references the code graph and infers product features from issue text.

Linear logo

Linear

Issues, projects, milestones, customers, initiatives → typed nodes. Edges back into the code graph. Features inferred from issue text.

The Linear connector ingests the issue-tracker side of the product surface and wires it back into the same workspace ontology that holds people, code, and meetings. An issue that mentions a file path edges to the code.file node; an issue classified as building a new product feature creates a typed feature node and points back at the issues that build it.

The connector is GraphQL-only — Linear's API surface is https://api.linear.app/graphql. OAuth token refresh is the same shape as the Google connectors; a 401 mid-sync triggers one refresh-and-retry, then surfaces as a connection error.

What gets ingested

Order of operations is deliberate — root nodes first so the downstream edges resolve on the same sync rather than waiting for the next pass:

  1. Customers — independent root nodes used as edge targets by issues that have a customer relation.
  2. Initiatives + projects (with their milestones) — anchor nodes that issues attach to via in_project / in_milestone.
  3. Cycles (optional) — sprint windows that issues attach to.
  4. Issues — the main payload. LLM enrichment runs per issue when enrich_with_llm is on.
  5. Status updates — project narrative posts that attach to the project node via about.
SourceNode typeIdentifierNotes
IssueissueLowercased identifier (e.g. oxa-235)Stable across re-syncs; cross-references in human prose resolve to the same node.
ProjectprojectLinear GUIDLinear ids don't surface in prose so GUID is fine.
MilestonemilestoneLinear GUIDEdges: in_milestone / has_milestone to projects.
InitiativeinitiativeLinear GUIDEdges: in_initiative / has_initiative_item to projects.
Cyclecyclelinear-cycle:<guid>Optional — disabled by default.
CustomercustomerEmail-domain · normalised name · linear:<id>See Customer dedup below.
Status updatestatus_updateLinear GUIDEdges: about to project, posted_update to author.
Teamteamlinear-team:<key>E.g. linear-team:eng.
LabellabelLowercased label nameEdges: has_label.
Person (assignee, creator, owner)personLowercased emailResolved via the shared person canonicaliser — same node Gmail and Calendar resolve to.

Edge inventory

EdgeSource → targetProvenance
assigned_to / created_byissuepersonStructural (issue assignee + creator)
in_project / has_issueissueprojectStructural
in_milestone / has_milestoneissuemilestone; projectmilestoneStructural
in_cycle / has_cycle_itemissuecycleStructural
in_initiative / has_initiative_itemprojectinitiativeStructural
for_customer / has_requestissuecustomerStructural
has_labelissuelabelStructural
in_teamissueteamStructural
related_to / blocks / blocked_by / duplicate_ofissueissueStructural (Linear's explicit relation graph)
aboutstatus_updateprojectStructural
posted_updatepersonstatus_updateStructural
builds / has_issueissuefeatureLLM-inferred — see Feature inference
referencesissuecode.file / documentLLM-inferred — gated on the target already existing in the workspace
related_toissueissue (beyond Linear's explicit relations)LLM-inferred from prose mentions of <team>-<n> identifiers

Edges into the code graph

The Linear connector is the bridge that fuses the issue surface to the code graph. Two of its edges are anchored on nodes already in the workspace ontology — the connector never invents the target:

  • references edges — when the LLM enrichment pass extracts a file path or filename from an issue's description (e.g. services/api/app/routes/billing.py), the connector looks up the code.file node by lowercased path. If the node exists, it writes an issue → code.file references edge. If the path matches no ingested file, the mention is silently dropped — we never mint a ghost code.file from an LLM hallucination.
  • Same shape for documents — document titles or page names the LLM extracts (e.g. Runbook: Billing migration) match against document nodes from the Drive connector or the workspace's documents browser, and write references edges to whatever already exists.

An agent answering "which open issues are blocking processOrder?" runs as a single Cypher hop in the workspace graph — no per-issue prose scan, no grep, no re-prompt:

MATCH (f:Node {type: 'code.function', name: 'processOrder'})
MATCH (file:Node {type: 'code.file'})-[:defines]->(f)
MATCH (i:Node {type: 'issue'})-[:references]->(file)
WHERE i.properties.state_type IN ['unstarted', 'started']
RETURN i.properties.identifier, i.properties.title, i.properties.url
ORDER BY i.properties.priority

Feature inference

When enrich_with_llm is on (default), each issue runs through a Haiku-class LLM call (FAST tier) that returns one structured JSON object — feature classification, referenced files, referenced documents, related issue identifiers, topical tags.

The feature classification is opinionated: bug fixes, refactors, chores → null. Feature gates, new endpoints, new UI surfaces → an object with name and summary.

When the LLM returns a feature, the connector:

  1. Creates or finds a feature node keyed on the feature name (case-insensitive, deduped via the workspace's standard name-resolution).
  2. Writes a builds edge from the issue to the feature.
  3. Writes the inverse has_issue edge from the feature back to the issue.

A feature surfaces every issue that contributes to it as one graph hop — "which issues build the SSO rollout?" becomes MATCH (f:Node {type: 'feature', name: 'sso rollout'})-[:has_issue]->(i). Pair the feature node with the code-graph traversal above and the agent can answer "which functions are touched by the issues that build SSO?" in one MCP call.

The LLM extraction is gated:

  • Mentions of file paths that match no ingested code.file → dropped.
  • Mentions of document titles that match no document → dropped.
  • Mentions of related-issue identifiers (e.g. eng-12) that match no ingested issue → dropped.

Hallucinations never write to the graph. The trade-off is the opposite of a fuzzy match — the connector errs on the side of silence rather than ghost nodes.

Customer dedup (cross-system)

Customer canonicalisation is first-class because Linear, Stripe, your CRM, and any future bridge will all mint a "customer" concept. The connector resolves on a deterministic order:

  1. Email domain. Linear's domains array — best cross-system key because CRMs, Stripe, and Linear all expose the customer's email domain or website.
  2. Normalised display name. Falls back when the record has no domain.
  3. linear:<linear_id>. Last resort so a nameless customer record never gets dropped.

The connector writes linear_id and linear_url as properties on the resolved customer node — so the same Acme account from Linear and from a future Stripe sync lands on one canonical customer node with both linear_id and stripe_customer_id attribution.

Settings

KeyTypeDefaultDescription
backfill_daysnumber180How far back to scan on the first sync. Subsequent syncs use the last_synced_at watermark.
ingest_cyclesbooleanfalseWhen true, ingest sprint cycles and add in_cycle / has_cycle_item edges. Disabled by default because most workspaces don't query at cycle granularity.
ingest_initiativesbooleantrueWhen true, ingest initiative anchors and in_initiative edges.
ingest_customersbooleantrueWhen true, ingest Linear customer records and write for_customer / has_request edges from issues.
enrich_with_llmbooleantrueWhen true, run the per-issue enrichment pass (feature inference, file / document references, tag extraction). Disable on large initial syncs to keep cost predictable.
max_issue_charsnumber12,000Truncate the issue body before stamping on the node and sending to the LLM.

OAuth scopes

  • read — read every Linear resource the user can see.
  • issues:create / issues:write are not requested.

Tokens are encrypted at rest with the workspace encryption key, rotated on every refresh, and revoked when the user disconnects the integration at Linear or in Oxagen.

Sync lifecycle

statusMeaning
pendingCreated but no successful sync yet. The first backfill is queued.
activeAt least one sync has completed. Incremental syncs run on the configured cadence.
errorThe most recent sync raised. last_error carries the message; retry is one click in the dashboard.
pausedOperator-paused. No automatic syncs run; manual refresh still works.

Mid-pagination failures leave last_synced_at and status alone so the next run retries from the same cutoff — same posture as the Zoom and Meet connectors.

Events and audit

Every sync publishes source.started, source.completed, and the standard node.created / edge.created events on the ontology bus — same surface as the rest of the connectors. Workspace agent triggers can subscribe to these and run prompted agents on issue create / update events. See Events, triggers, and audits.


Get started free · Connectors overview · Code Graph · Events, triggers, and audits

On this page