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
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:
- Customers — independent root nodes used as edge targets by
issues that have a
customerrelation. - Initiatives + projects (with their milestones) —
anchor nodes that issues attach to via
in_project/in_milestone. - Cycles (optional) — sprint windows that issues attach to.
- Issues — the main payload. LLM enrichment runs per issue
when
enrich_with_llmis on. - Status updates — project narrative posts that attach to
the project node via
about.
| Source | Node type | Identifier | Notes |
|---|---|---|---|
| Issue | issue | Lowercased identifier (e.g. oxa-235) | Stable across re-syncs; cross-references in human prose resolve to the same node. |
| Project | project | Linear GUID | Linear ids don't surface in prose so GUID is fine. |
| Milestone | milestone | Linear GUID | Edges: in_milestone / has_milestone to projects. |
| Initiative | initiative | Linear GUID | Edges: in_initiative / has_initiative_item to projects. |
| Cycle | cycle | linear-cycle:<guid> | Optional — disabled by default. |
| Customer | customer | Email-domain · normalised name · linear:<id> | See Customer dedup below. |
| Status update | status_update | Linear GUID | Edges: about to project, posted_update to author. |
| Team | team | linear-team:<key> | E.g. linear-team:eng. |
| Label | label | Lowercased label name | Edges: has_label. |
| Person (assignee, creator, owner) | person | Lowercased email | Resolved via the shared person canonicaliser — same node Gmail and Calendar resolve to. |
Edge inventory
| Edge | Source → target | Provenance |
|---|---|---|
assigned_to / created_by | issue → person | Structural (issue assignee + creator) |
in_project / has_issue | issue ↔ project | Structural |
in_milestone / has_milestone | issue ↔ milestone; project ↔ milestone | Structural |
in_cycle / has_cycle_item | issue ↔ cycle | Structural |
in_initiative / has_initiative_item | project ↔ initiative | Structural |
for_customer / has_request | issue ↔ customer | Structural |
has_label | issue → label | Structural |
in_team | issue → team | Structural |
related_to / blocks / blocked_by / duplicate_of | issue ↔ issue | Structural (Linear's explicit relation graph) |
about | status_update → project | Structural |
posted_update | person → status_update | Structural |
builds / has_issue | issue ↔ feature | LLM-inferred — see Feature inference |
references | issue → code.file / document | LLM-inferred — gated on the target already existing in the workspace |
related_to | issue ↔ issue (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:
referencesedges — 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 thecode.filenode by lowercased path. If the node exists, it writes anissue → code.filereferencesedge. If the path matches no ingested file, the mention is silently dropped — we never mint a ghostcode.filefrom an LLM hallucination.- Same shape for documents — document titles or page names
the LLM extracts (e.g.
Runbook: Billing migration) match againstdocumentnodes from the Drive connector or the workspace's documents browser, and writereferencesedges 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.priorityFeature 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:
- Creates or finds a
featurenode keyed on the feature name (case-insensitive, deduped via the workspace's standard name-resolution). - Writes a
buildsedge from the issue to the feature. - Writes the inverse
has_issueedge 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 ingestedissue→ 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:
- Email domain. Linear's
domainsarray — best cross-system key because CRMs, Stripe, and Linear all expose the customer's email domain or website. - Normalised display name. Falls back when the record has no domain.
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
| Key | Type | Default | Description |
|---|---|---|---|
backfill_days | number | 180 | How far back to scan on the first sync. Subsequent syncs use the last_synced_at watermark. |
ingest_cycles | boolean | false | When 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_initiatives | boolean | true | When true, ingest initiative anchors and in_initiative edges. |
ingest_customers | boolean | true | When true, ingest Linear customer records and write for_customer / has_request edges from issues. |
enrich_with_llm | boolean | true | When 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_chars | number | 12,000 | Truncate 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:writeare 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
status | Meaning |
|---|---|
pending | Created but no successful sync yet. The first backfill is queued. |
active | At least one sync has completed. Incremental syncs run on the configured cadence. |
error | The most recent sync raised. last_error carries the message; retry is one click in the dashboard. |
paused | Operator-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
GitHub
Sync a GitHub repository into the workspace knowledge graph as a typed code-graph — files, functions, classes, imports, calls, commits — keeping the graph fresh on every push.
Plaid
Connect bank and credit-card accounts via Plaid Link. Transactions, accounts, and merchants land as typed `financial.*` nodes with the edges that make money-movement graph-traversable.