@linear/sdk works against it unchanged with apiUrl pointed at the emulator’s /graphql. Reads and writes use in-memory state.
At a glance
Start
http://localhost:4011 when all services start; started alone, it uses http://localhost:4000. Without a seed config there is a Baseline organization, an admin ([email protected]) and a developer ([email protected]), an ENG team with Bug and Feature labels, a project and a cycle, an OAuth app (lin_example_client_id), and the token lin_test_admin.
Point @linear/sdk at the emulator’s /graphql with apiUrl:
URL mapping
Auth
GraphQL acceptsAuthorization: Bearer <token> or a bare personal API key value. The default seeded token is lin_test_admin. Seeded tokens map to users or app actors.
Scope checks are relaxed by default. Set linear.strict_scopes: true to require supported operation scopes such as read, write, issues:create, comments:create, and admin.
OAuth
GET /oauth/authorize— Authorize: local user pickerPOST /oauth/authorize/callback— User picker callback: local callback used by the user pickerPOST /oauth/token— Exchange token: authorization code, refresh token, and client credentials grantsPOST /oauth/revoke— Revoke token: access or refresh tokens
actor=app tokens for local app-actor tests. OAuth apps can use actor: user or actor: app. The configured actor is authoritative. User actor apps use authorization code flows. App actor apps use the app install flow and can request client credentials tokens.
GraphQL
POST /graphql— Run a GraphQL query or mutationGET /graphql— Run a GraphQL query from the query string: for tooling. Queries only; a mutation over GET gets HTTP 405
@linear/sdk, so queries are validated as Linear validates them: a field, argument, or input key Linear does not have fails with HTTP 400. Any GraphQL error answers HTTP 400. The endpoint runs on the shared GraphQL kit.
Supported queries:
viewerorganizationusers,userteams,team,teamMembershipworkflowStates,workflowStateissues,issuecomments,commentissueLabels,issueLabelprojects,projectcycles,cyclewebhooks,webhookagentSessions,agentSession
issueCreate,issueUpdate,issueDelete,issueArchive,issueUnarchivecommentCreate,commentUpdate,commentDeleteissueLabelCreate,issueLabelUpdate,issueLabelDelete(label names are unique, ignoring case, within a team and against workspace labels:Duplicate label name)issueAddLabel,issueRemoveLabelwebhookCreate,webhookDeleteagentSessionCreateOnIssue,agentSessionCreateOnComment,agentSessionUpdateagentActivityCreate
initiatives or documents) fails with <Type>.<field> is not emulated by baseline.
Connections use Relay-style cursors with nodes, edges, and pageInfo.
Fields the emulator does not model
@linear/sdk selects every scalar field of a model, so the emulator answers the whole schema. A field it does not model reads as an empty workspace would have it: null when nullable, otherwise false, 0, an empty string, list, or JSON object, or an empty connection. A non-null field that has no empty value (an object, enum, or date) fails with <Type>.<field> is not emulated by baseline rather than inventing one.
Issue selections expose both numeric priority and Linear’s derived priorityLabel values: No priority, Urgent, High, Medium, and Low. Issues and comments created with createAsUser return a botActor with that name and displayIconUrl.
Teams expose viewerMembership, viewerCanJoin, and viewerCanLeave. Team membership is not modeled, so the viewer counts as a member of every team. Initiatives are not modeled either, so ledInitiativeCount is always 0 and initiativesEnabled is false.
Filters and ordering
Connections take Linear’s filter inputs (IssueFilter, TeamFilter, UserFilter, and so on) with their comparators: eq, neq, in, nin, null, lt, lte, gt, gte, the string comparators (contains, startsWith, containsIgnoreCase, and the rest), and and / or. Relations filter through the related entity (team: { key: { eq: "ENG" } }), and collections take some, every, and length (labels: { some: { name: { eq: "Bug" } } }). Date comparators take ISO dates or durations relative to now, such as -P2W. An issue id comparator matches the UUID or the identifier (ENG-12).
A filter key the schema has but the emulator does not model fails with IssueFilter.<key> is not emulated by baseline rather than matching everything. orderBy takes createdAt (the default) or updatedAt.
Issue lists leave out archived and trashed issues unless the query passes includeArchived: true, and user lists leave out deactivated users unless it passes includeDisabled: true.
Mutations
issueUpdate(id:, input:)acceptslabelIds, oraddedLabelIdsandremovedLabelIdsto change labels in place.issueDeletemoves the issue to the trash (archived, withtrashed: true), as Linear does.issueDelete(id:, permanentlyDelete: true)removes it with its comments and agent sessions, and needs an admin.webhookCreaterequiresresourceTypes.- A single lookup by an unknown ID fails with
Entity not found: <Type>. - An input key Linear accepts but the emulator does not model (for example
IssueCreateInput.estimate) fails with<Input>.<key> is not emulated by baselineinstead of being dropped.
Agent sessions
Agent sessions follow Linear’s agent API. A session belongs to an app user: the caller when it authenticates as an app (actor: app), otherwise the first seeded app user, otherwise the caller. Sessions start pending. Activities move them:
agentActivityCreate takes agentSessionId and content, and validates content per type (an action needs action and parameter; the others need body). Only thought and action activities may be ephemeral. agentSessionUpdate sets plan, summary (one line, 1 to 255 characters), externalLink, and externalUrls, addedExternalUrls, or removedExternalUrls.
Creating a session, delegating an issue to an app user, or mentioning an app user in a comment sends an AgentSessionEvent webhook with action: created. A user’s prompt activity sends action: prompted with the activity.
Errors
Every error carries Linear’sextensions.type, which @linear/sdk maps to its error classes: invalid input (InvalidInputLinearError) for bad input and unknown IDs, forbidden (ForbiddenLinearError) for a missing scope, graphql error (GraphqlLinearError) for a query the schema rejects, and feature not accessible (FeatureNotAccessibleLinearError) for a field the emulator does not model, which also keeps extensions.code: NOT_EMULATED.
Webhooks
Create local webhook subscriptions throughwebhookCreate or seed config. Supported writes dispatch Linear-shaped payloads with these headers:
Linear-DeliveryLinear-EventLinear-Signature
Seed config
Inspector
GET /— Inspector: tabbed view of issues, teams, users, projects, agent sessions, OAuth apps, tokens, webhook subscriptions, and webhook deliveries
Limits
Resolvers for the root fields not listed above, exact production rate limiting, notification inbox behavior, rich document APIs, customer APIs, initiative APIs, exact search relevance, and full production agent behavior are not implemented.Conformance
See CONFORMANCE.md for every provider.