Sync Linear issues and comments into Convex in real time via webhooks, and drive Linear from Convex actions using the GraphQL API.
npm install convex-linearconvex-linear keeps a live, queryable mirror of Linear issues and comments inside your Convex deployment, updated in real time via verified webhooks. It also exposes actions for creating, updating, commenting on, archiving, and unarchiving issues directly through Linear's GraphQL API. The component's tables live in an isolated schema so they do not conflict with your app's own data model.
convex-linear mounts a webhook handler at any HTTP route in your Convex deployment and subscribes to Linear's Issue and Comment resource types. Every delivery upserts the corresponding row in the component's isolated issues or comments table, so a useQuery call like listIssuesByTeam re-renders automatically in your React app whenever an issue changes in Linear, including changes made from Linear's own UI.
Instantiate the Linear client with your API key and webhook secret, then call linear.createIssue(ctx, { teamId, title, description, priority }) inside a Convex action. The method calls Linear's GraphQL API, records the full issue in Convex immediately, and returns { identifier, url } without waiting for a webhook round-trip.
convex-linear's built-in webhookHandler performs constant-time HMAC-SHA256 verification against the Linear-Signature header and rejects any delivery whose Linear-Timestamp is more than 60 seconds old before writing anything to the database. You mount it with http.route and the component handles the rest.
Call linear.archiveIssue(ctx, { issueId }) from a Convex action. The method archives the issue in Linear via the GraphQL API and sets the archivedAt field on the issue's Convex row rather than deleting the row, so the issue remains queryable with its archived state visible.
No. convex-linear is a Convex component, so its issues, comments, and webhookEvents tables live in an isolated component schema entirely separate from your app's own schema. You access them only through the methods the component exposes, not through direct table queries.
convex-linear records every webhook delivery by its Linear-Delivery ID before processing it. If the same delivery ID arrives again, the handler skips processing, so retried deliveries never double-insert or double-update rows in your Convex database.
On every Issue webhook delivery, convex-linear re-fetches the issue's current state from Linear's GraphQL API and upserts the result including archivedAt and trashed fields. The Convex row is only hard-deleted if the re-fetch returns an Entity not found error, meaning the issue is permanently gone. This approach is necessary because Linear's webhook action field does not reliably distinguish archived, trashed, and deleted states.
You need to subscribe to the Issues and Comments resource types when creating the webhook in Linear's Settings under API and Webhooks. The webhook should point to the route where you mounted linear.webhookHandler in your convex/http.ts file, typically at https://<your-deployment>.convex.site/webhooks/linear.
Yes. Call linear.updateIssue(ctx, { issueId, stateId }) from a Convex action, passing the workflow state's UUID as stateId. Linear workflow states are team-specific, so you need to obtain the state UUID from Linear's GraphQL API or from the URL in Linear's workflow settings rather than using a fixed status string.