Skip to main content
Connect a Linear workspace to Cognee by installing a Linear OAuth app into it as an agent. Workspace members can then @mention the agent or delegate an issue to it and get an answer drawn from your Cognee memory, while the workspace’s issues flow into a dataset of their own and stay fresh through webhooks.
This integration is for self-hosted Cognee deployments — you create and own the Linear OAuth app, so it lives in your own Linear workspace settings and talks only to your own Cognee backend.
There are two Linear surfaces — this page is the agent one. It covers the workspace-level OAuth agent: @mentions, delegated issues, and webhook-fed issue indexing, with Cognee holding the credential. To sync one team’s issues, comments, and projects from your own code with a token you already hold, use the SDK’s linear_source() connector instead — it needs no extra and no OAuth app. Ships in 1.5.4. The connector landed after 1.5.3 and is first tagged in v1.5.4, so cognee>=1.5.4 carries it — no dev checkout is needed. See the changelog.

What It Does

  • Installs as an agent, not a bot user. The authorize URL carries actor=app, which puts an app user — the agent’s identity — into the workspace. The app:assignable and app:mentionable scopes are what let members delegate issues to it and @mention it. Requested scopes are read,write,app:assignable,app:mentionable.
  • Answers agent sessions from memory. A mention or a delegation opens an agent session; Cognee runs a HYBRID_COMPLETION search across every dataset the connecting user can read — not just the Linear one — and replies inside the session.
  • Indexes the workspace’s issues. Issue creates and updates are remembered as plain text in one dataset per workspace, plus a backfill of recent issues at install time.
  • One workspace, one Cognee user. The credential is owned by whoever completed the install, and answers come from that user’s memory.

Prerequisites

  • Cognee >= 1.5.4 — see the version note above.
  • A running Cognee backend reachable over HTTPS from the public internet: Linear delivers webhooks and the OAuth redirect to it and will not call localhost. For local development use a tunnel (ngrok or similar) — see the Slack integration’s ngrok section, which applies verbatim; for production this is your deployment’s normal public domain.
  • Permission to create an OAuth application in the target Linear workspace.

Setup

1. Create the Linear OAuth app

Go to linear.app/settings/api/applications → create a new application and configure: Then collect the app’s Client ID and Client secret.
Create a separate app per environment (local dev, staging, production). One app has one webhook URL, so pointing a shared app at a laptop routes another environment’s deliveries there.

2. Configure Cognee

Copy the app’s values into your backend’s .env (the same block is in .env.template): Every one of these is checked at use time, not at boot: a deployment with Linear unconfigured still starts normally, and the first call that needs a missing value fails with LINEAR_<NAME> is not configured rather than a confusing downstream error. POST /authorize turns that into a 503 (linear integration is not configured on this server) instead of a 500. Restart the backend after any .env change.

3. Install and connect

The backend ships without frontend wiring for Linear in this release — the aggregate GET /api/v1/integrations/status reports the provider generically, but there is no Connect button yet. Drive the flow through the API:
Open that URL in a browser, pick the workspace, and approve. Linear redirects back to /api/v1/integrations/linear/callback, which binds the workspace to your Cognee account and then sends the browser to <LINEAR_FRONTEND_BASE_URL>/integrations?linear=connected.
Install from the minted URL. The signed state is what binds the install to a Cognee account, and it expires 10 minutes after /authorize returns. An install started without it (or after it expires) lands on ?linear=error_invalid_state and stores nothing.
The callback does one extra round trip before it redirects: Linear’s token response says nothing about which workspace authorized the app, but every webhook delivery routes by its organizationId, so the fresh token is immediately spent on a GraphQL query for viewer (the newly installed app user) and organization. The organization id becomes the credential’s account id; the organization name becomes its display label. The initial issue backfill is kicked off detached, after the redirect — the browser returns immediately.

What Gets Indexed

Issues land in one dataset per workspace, named linear_<url_key>: the workspace’s Linear URL key (the slug in linear.app/<url_key>) lowercased, with every run of characters outside A-Z a-z 0-9 _ collapsed to _. A workspace at linear.app/acme-corp becomes linear_acme_corp. One dataset per workspace rather than per team or per issue means backend access control isolates at the workspace boundary — per-issue datasets would mean one isolated database per issue. Each issue is stored as a deterministic plain-text rendering — identifier, title, URL, state, description, and nothing volatile — so re-syncing an unchanged issue produces byte-identical text instead of churning the graph.
At install time Cognee backfills the workspace’s 50 most recently updated issues in a single remember() call — webhooks only cover changes from then on, so this is what gives the agent something to recall from on day one. Because it is ordinary text ingestion, the content is reachable through the normal search types, and you can query the dataset directly:
Both the backfill and per-issue webhook syncs run with self_improvement=False, so they never trigger an improve() pass. Whole-graph enrichment carries LLM cost that is far too heavy to fire on every issue edit — it stays a human or scheduled decision.

Answering Agent Sessions

An agent session opens when a member @mentions the agent (AgentSessionEvent / created) or delegates an issue to it, and continues when they reply inside the session (prompted). For each turn Cognee:
  1. Posts a thought activity immediately — “Searching cognee memory…” — before any search or LLM work. Linear requires an activity within 10 seconds of a created event or it marks the session unresponsive, and a HYBRID_COMPLETION search routinely takes longer than that.
  2. Builds the query. Linear’s own promptContext (its digest of the issue and prior comments) and workspace-level guidance are prepended to the user’s question, so retrieval is grounded in what the session is about. On a prompted event the question is the new message; on a created event it is the triggering comment, or — for a delegation, which has no comment — the issue’s title and description.
  3. Searches memory with HYBRID_COMPLETION across every dataset the connecting user can read, keeping the top three answers and discarding per-chunk completions that are really refusals (“no relevant information…”).
  4. Posts a response activity with the answer, which completes the turn.
Every failure path ends in an error activity rather than silence, so a session never sits showing the agent as working forever. Two cases answer politely instead of erroring: no question could be found in the session, and nothing relevant in memory (“No relevant information found in cognee memory.”).

Keeping It in Sync

Webhook deliveries to POST /api/v1/integrations/linear/events drive everything after the install. Coverage is deliberately narrow in this first cut: Everything else — issue deletions included — is logged and dropped. Deleting indexed data on a webhook would be a silent, destructive surprise, so forget() stays a human decision. A delivery for a workspace Cognee doesn’t know (or has revoked) is logged and dropped rather than erroring — Linear gives no ordering guarantee, so a delivery racing ahead of the callback storing the credential is a normal, self-healing state.

Token Lifetime and Refresh

A Linear access token lasts about 24 hours, and Linear rotates the refresh token: every refresh hands back a new one and kills the one just spent. Cognee handles both — there is nothing to configure and no reconnect to schedule.
  • Refreshed just in time, not on a timer. Every Linear API call — answering an agent session, syncing an issue — goes through one code path that refreshes the stored token when it is within 5 minutes of expiring. The margin is there so a sync that takes a while doesn’t expire halfway through its own issue list, and the refresh itself has a short timeout so it fits inside the agent session’s 10-second acknowledgement budget.
  • One refresh at a time per workspace. Concurrent callers who all find the token expiring don’t each spend the same refresh token: they serialize behind one refresh, and the write back to storage is a compare-and-swap against the stored token, so a second backend process or a reconnect landing at the same moment cannot be overwritten.
  • The stored refresh token is replaced on every refresh. The response’s new refresh token is what gets saved — carrying the old one forward would make the second refresh fail and disconnect the workspace about two days after it connected.
  • A Linear 401 also triggers a refresh. The stored expiry can be wrong, or Linear can end a token early, so a rejected token forces a refresh even when the expiry looks fine, and the call is retried once. A token that a forced refresh minted less than a minute ago and Linear still rejects is not rotated again — nothing a new token can fix is wrong.
  • A transient failure falls back to the token that still works. Inside the 5-minute margin the stored token is still valid, so a refresh that times out or hits a gateway error keeps using it, and one more attempt runs about a minute later (which also recovers a rotation Linear performed but Cognee lost). Callers arriving in the next few seconds reuse that recorded failure instead of each sending their own doomed request.
  • A rejected refresh token is terminal. Linear answering invalid_grant means the stored refresh token is dead and only a reconnect brings it back, so Cognee revokes the credential locally rather than leaving a connection that reads as healthy while every session fails. See the troubleshooting entries below.

Webhook Security

Every delivery is authenticated by a hex HMAC-SHA256 over the raw request body, keyed with LINEAR_WEBHOOK_SECRET and compared against the Linear-Signature header with a constant-time comparison. That signature is the entire auth model for the route — it is unauthenticated by design, since Linear cannot send a bearer token. A bad or missing signature is a 401. Linear also carries replay protection, which Cognee enforces: the payload’s webhookTimestamp (Unix milliseconds) must be within 60 seconds of the server’s clock, or the delivery is rejected as Stale Linear webhook timestamp — also a 401. A missing or malformed timestamp is rejected the same as a stale one, and the check runs only after the HMAC passes, since a timestamp read from unverified bytes proves nothing. Two consequences worth planning for:
  • Keep the Cognee host’s clock in sync (NTP). More than a minute of drift rejects every delivery even though its signature is perfect.
  • Rotating the signing secret is a two-sided change. Deliveries signed with the old secret fail the HMAC, so rotate on Linear and update LINEAR_WEBHOOK_SECRET together and restart the backend. Because the same secret also signs the OAuth state, an install that is mid-flight during a rotation lands on ?linear=error_invalid_state; just mint a fresh authorize URL.
Cognee acks the delivery as soon as both checks pass and handles it detached, so Linear’s delivery timeout is never in play no matter how long a search takes. The 10-second rule is separate — it is Linear’s clock on the session, which only the thought activity satisfies.

Disconnect vs. Uninstall

Neither deletes indexed data:
  • Disconnect in Cognee — DELETE /api/v1/integrations/linear/connection — marks the stored credential revoked, so agent sessions and issue syncs stop. It also makes a best-effort remote revoke: Linear exposes a cheap token-revoke endpoint that kills only Cognee’s own tokens without touching the app install, so unlike the GitHub connector this is actually called. Both stored tokens are revoked — the refresh token first, then the access token — since Linear doesn’t document whether revoking one ends the other. It posts the tokens it already holds rather than refreshing first (by disconnect time the access token has usually expired anyway, and a refresh would only add a way for this to fail). It is best effort by design — the local revoke is the real cut-off, and a network blip must never block a disconnect.
  • Removing the app in Linear fires the OAuthApp revoked webhook, which revokes the credential server-side — the same end state, reached from the other side.
The linear_<url_key> dataset stays exactly as it is in both cases. To remove the indexed data itself, use forget().

Troubleshooting

  1. POST /authorize returns 503 “linear integration is not configured on this server”: LINEAR_CLIENT_ID, LINEAR_REDIRECT_URI, or LINEAR_WEBHOOK_SECRET (it signs the state) is unset. The backend log names the exact variable. A missing LINEAR_FRONTEND_BASE_URL surfaces later, as the same 503 from the callback instead.
  2. Redirected to ?linear=error_invalid_state: the install didn’t start from a freshly minted /authorize URL, more than 10 minutes passed before approving it, or LINEAR_WEBHOOK_SECRET changed in between. Mint a new one.
  3. Redirected to ?linear=error_exchange_failed: the callback never 500s — it catches everything and redirects, and the traceback is in the backend log. Usual causes: mismatched LINEAR_CLIENT_ID/LINEAR_CLIENT_SECRET, a LINEAR_REDIRECT_URI that doesn’t exactly match the app’s registered callback URL, a missing INTEGRATION_CREDENTIALS_KEY, or the identity query failing (Linear token response carries no organization id).
  4. Redirected to ?linear=error_already_connected: this workspace is bound to another Cognee user. Disconnect it there first.
  5. Redirected to ?linear=cancelled: the consent screen was cancelled, or Linear rejected the request. Nothing was stored.
  6. Connect succeeds but no issues appear: the backfill runs detached — check the backend log for Syncing N Linear issues for organization <id> into dataset linear_<url_key>, or has no issues to sync. It covers the 50 most recently updated issues only; everything older arrives when those issues are next touched.
  7. Webhook deliveries return 401: either LINEAR_WEBHOOK_SECRET doesn’t match the app’s signing secret (easy to hit when juggling more than one app), or the host clock has drifted more than 60 seconds. The response detail distinguishes the two — Invalid Linear signature versus Stale Linear webhook timestamp.
  8. Webhook deliveries return 404: the provider name in the URL is wrong, or the backend is running a build without the Linear connector registered. The route is /api/v1/integrations/linear/events.
  9. The agent posts an error activity in the session: the search or the reply failed — the backend log carries the traceback under Linear agent session <id>: answering failed. If the answer instead says the connection “is not fully configured”, the Cognee user who installed the integration was deleted; disconnect and reconnect.
  10. The agent replies “No relevant information found in cognee memory.”: the search returned nothing usable for that question. Remember that it searches the installing user’s datasets — content ingested by a different Cognee user is not visible to it.
  11. The connection flipped to disconnected on its own: Linear rejected the stored refresh token (invalid_grant), which is terminal — the backend log carries Linear rejected the refresh token for organization <id>; local credential revoked, the workspace must reconnect. Reconnect from /authorize; nothing else recovers it. The log also distinguishes a credential that holds no refresh token at all (holds no refresh token; the workspace must reconnect) — a connection whose install returned no refresh token. That credential is not revoked, so it still reads as connected: it keeps working until its access token expires and then needs the same reconnect.
  12. Every refresh fails right after a LINEAR_CLIENT_SECRET rotation: Linear is rejecting the app’s own credentials (invalid_client and friends), logged as Linear rejected the app's own credentials refreshing the token for organization <id>. These are never retried — no retry can fix a wrong secret — and the failure is remembered for 5 minutes so every caller doesn’t resend the same doomed request. Put the current Client ID and Client secret in .env and restart the backend: they’re read at start-up, so an edit alone changes nothing.
  13. Occasional using the token that is still valid warnings: a refresh timed out or the token endpoint answered with an error, inside the 5-minute margin where the stored token still works. For a timeout or gateway error this is the designed fallback, not a failure — one retry runs about a minute later. The same warning is logged when the refresh fails on a configuration error or a missing refresh token, which are not retried, so act on a run of them that comes with the entry 11 or entry 12 log lines.

Slack

Ask and save memory from a conversation, sharing the same credential store and connect flow.

GitHub

The other first-party webhook connector — an org’s repositories into the code graph.