Skip to content →
Sign upOpen app

Developing agents

This guide describes how to best integrate an AI agent into Linear. It includes implementation guidelines on how to design an experience that feels native to Linear’s workflows and interaction patterns.

Developer Preview

Linear for Agents APIs are currently in active development and available as a Developer Preview. Functionality and Agent APIs may change before general availability.

Overview

Agents behave similar to other users in a workspace. They can be @mentioned, delegated issues through assignment, create and reply to comments, collaborate on projects and documents, etc. App users are installed and managed by workspace admins.

You can build agents for internal use within your own workspace or for distribution to other organizations. It does not cost anything to develop agents in Linear. To make your agent available to other workspaces, submit your agent to Linear's integration directory.

Additionally, agents installed in your workspace do not count as billable users.

We've created a demo agent built on our Typescript SDK and Cloudflare, if you want to dive straight into an example codebase.

Weather Bot is an agent that will help you look up the weather of any location within a Linear issue.

Setup

Create a new Application and configure the settings as you would for a standard OAuth application.

In the configuration, enable webhooks and make sure to select Agent session events at the bottom. Enabling this category will notify your webhook when events occur that are directly relevant to your app's user.

Note that the name and icon of your application will be how the agent appears in workspaces where it is installed (e.g. in the mention and filter menus), so it is best to choose something short, recognizable, but unique.

Authentication

Actor and scopes

App authentication is built on top of the standard OAuth2 flow. To install your agent into a Linear workspace in the OAuth authorization url add the actor=app parameter to switch to an app installation rather than requesting authentication as the installing user. Because this will be installed with a workspace scope admin permissions are required to complete the installation.

This new actor type supersedes any references to actor=application and can be used for all agent, app, and service account use-cases.

Mention + assign scopes

To allow for flexibility, the ability to mention and assign your agent is optional and must be requested through the use of two new additional scopes added to the scope query parameter:

ScopeDescription
app:assignableAllow the app to be assigned as a delegate on issues and made a member of projects
app:mentionableAllow the app to be mentioned in issues, documents, and other editor surfaces

Assigning an issue to your app now sets it as the delegate, not the assignee—so humans maintain ownership while agents act on their behalf.

Admin

Note that integrations using the actor=app mode are not able to also request admin scope.

Installation

Your app will have a unique ID for each workspace it is installed within, you can find this ID with the following query using the OAuth access token received as part of the installation flow:

query Me {
  viewer {
    id
  }
}

We highly recommend storing this ID alongside your access token so that you can confidently identify your app in different workspaces.

Management

The team access available to your app can be changed or revoked at any time by workspace admins. If you're subscribed to the Permission changes webhook category, a PermissionChange webhook will be sent when access changes occur.

Agent Interaction

Agent status and activity can be surfaced natively in Linear through two main primitives:

Agent Session

AgentSession tracks the lifecycle of an agent run. Session states let the user know if the agent is currently working, waiting for user input, in an error state, or has finished work. An AgentSession is created automatically when an agent is mentioned or delegated an issue.

Agent Session State

Agent sessions can have one of 5 states: pending, active, error, awaitingInput, complete. These will be visible to users.

You don’t need to manage agent session state manually. Linear tracks session lifecycle automatically based on the last emitted activity.

Agent Activity

Agents communicate progress by emitting semantic agent activities to an AgentSession. These activities can represent thoughts, tool calls, prompts for clarification, final responses, or errors.

Agent Session Webhooks

An AgentSession webhook is sent to notify your agent when it's mentioned, delegated an issue through assignment, or when a user provides additional prompts.

To receive these events, enable the agent session events webhooks category in your OAuth application configuration.

You must acknowledge receipt of a webhook within 5 seconds.

Once you subscribe to AgentSessionEvent webhooks, customers will begin seeing Agent Session UI in Linear. This happens as soon as the event category is enabled, even if you’re only listening for debugging purposes.

If you receive a created event, you are expected to respond within the 10 seconds to avoid the session being marked as unresponsive.

Agent Session Webhook Events

AgentSessionEvent webhooks only send events to your specific agent.

There will be two types of actions in the AgentSessionEvent category, denoted by the action field of the payload:

ActionBehavior
createdA new Agent Session has been created (triggered by a user mention or delegation). You should start a new agent loop in response. Relevant input may be included in the agentSession.issue, agentSession.comment, or agentSession.previousComments body. Your agent can use all of this context to determine what action to take.
promptedA user sent a new message into an existing Agent Session. You should insert that message into the conversation history and take action. You should mainly pay attention to the `agentActivity` field’s body, as the user’s input is usually located there.

View Session on External URL

You can set an externalUrl on an AgentSession so users can open the current session on your web dashboard.

Use the agentSessionUpdateExternalUrl mutation to set this value. Pass null to remove it.

Sending Agent Activities

Agents should communicate progress by emitting Agent Activities to Linear. These activities can represent thoughts, actions, prompts for clarification, final responses, or errors.

You can emit activities using either the TypeScript SDK or a direct GraphQL mutation:

TypeScript SDK

const { success, agentActivity } = await linearClient.createAgentActivity({
  agentSessionId: "...",
  content: {
    type: "...", 
    ... // other payload fields - see below
  },
});

GraphQL

# Operation
mutation AgentActivityCreate($input: AgentActivityCreateInput!) {
  agentActivityCreate(input: $input) {
    success
    agentActivity {
      ...
    }
  }
}
 
# Variables
{
	"input": {
		"agentSessionId": "...",
		"content": {
			"type": "...",
			... # other payload fields - see below
		} 
	}
}
# Variables
{
	"input": {
		"agentSessionId": "...",
		# Shape of content varies by activity type
		# See below for more details
		"content": {
			"type": "...",
			... # other payload fields
		} 
	}
}

Activity Content Payload

Your agent may emit one of five allowed activity types. These are validated server-side, and invalid shapes will be rejected. Unless otherwise noted, all fields shown are required. Markdown is supported in body fields.

A thought or internal note.

{
  "content": {
    "type": "thought",
    "body": "The user asked about the weather."
  }
}

Requests clarification or confirmation from the user.

{
  "content": {
    "type": "elicitation",
    "body": "Where are you located? I will find the current weather for you"
  }
}

Describes a tool invocation. You may optionally include a result if the action has completed.

Without result (starting an action):

{
  "content": {
    "type": "action",
    "action": "Searching",
    "parameter": "San Francisco Weather",
    // "result": undefined (optional)
  }
}

With result (after completion):

{
  "content": {
    "type": "action",
    "action": "Searched",
    "parameter": "San Francisco Weather",
    "result": "12°C, mostly clear" // Markdown OK
  }
}

Indicates work has been completed or a final result is available.

{
  "content": {
    "type": "response",
    "body": "The weather in San Francisco is currently **foggy**, no surprise there."
  }
}

Used to report an error or failure.

{
  "content": {
    "type": "error",
    "body": "Out of credits. [Pay up!](https://agent.com/pay)"
  }
}

Additionally, you may see references to a prompt type AgentActivity. That is a user-generated message, usually as a follow-up prompt or responding to an elicitation. These are the messages that emit a prompted webhook to you on creation.

An agent cannot generate a prompt type activity.

Interaction best practices

Linear users have high expectations for the quality and consistency of the experience inside Linear. We aim to extend this to agents, which should act in a predictable and natural manner.

Recommendations

Upon receiving the created webhook, your agent should respond immediately with a thought activity to acknowledge that the agent has started working. This lets the user know right away that their prompt has been received.

The first response must be sent within 10 seconds of receiving the created event, or the agent will be shown as unresponsive.

Follow-up activities after the first response can still be sent for up to 30 minutes before the session is considered stale. Note that this stale state is recoverable by sending another agent activity.

If your agent is delegated to work on an issue that is not in a started, completed, or canceled status type, move the issue to the first status in started when your agent begins work.

When work is complete, emit an AgentActivity with type response; or if you require additional actions from the user, emit an AgentActivity with type elicitation or error. We will automatically create a comment under the comment thread as well.

Agent Activities

query AgentSession($agentSessionId: String!) {
  agentSession(id: $agentSessionId) {
    activities {
      edges {
        node {
          updatedAt
          content {
            ... on AgentActivityThoughtContent {
              body
            }
            ... on AgentActivityActionContent {
              action
              parameter
              result
            }
            ... on AgentActivityElicitationContent {
              body
            }
            ... on AgentActivityResponseContent {
              body
            }
            ... on AgentActivityErrorContent {
              body
            }
	        ... on AgentActivityPromptContent {
              body
            }
          }
        }
      }
    }
  }
}

// @linear/sdk@^53.0.0
const agentSessionActivities = await agentSession.activities();
agentSessionActivities.nodes.forEach(activity => {
	switch (activity.content.__typename) {
		// type narrowing
		case "AgentActivityThoughtContent":
			const { body } = activity.content;
			...
			break;
		case "AgentActivityActionContent":
			const { action, parameter, result } = activity.content;
			...
			break;
		case "AgentActivityElicitationContent":
			const { body } = activity.content;
			...
			break;
		case "AgentActivityResponseContent":
			const { body } = activity.content;
			...
			break;
		case "AgentActivityErrorContent":
			const { body } = activity.content;
			...
			break;
		case "AgentActivityPromptContent":
			const { body } = activity.content;
			...
			break;
		default:
			throw Error("Not reachable")
	}
})

Comments may not be reliable to read from, as they are editable and may have changed since your agent’s last run. Agent Activities are frozen-in-time snapshots of user input. To reconstruct the full conversation, list the Agent Activities associated with the Agent Session instead—see above for examples.

Additional Webhooks

In addition to the core AgentSession webhooks, there are additional webhooks that your agent can listen to in order to build a richer agent experience within Linear. In addition, you can utilize any of the existing GraphQL APIs.

Inbox Notifications Webhooks

Inbox Notification events are triggered when something directly involves your app user—like when an agent is unassigned from an issue or a user reacts to a comment from the agent.

Enable this category by selecting Inbox Notifications in your OAuth app config.

The received webhook payload will have the following shape:

{
  type: "AppUserNotification",
  action: NotificationType,
  createdAt: string,
  organizationId: string,
  oauthClientId: string,
  appUserId: string,
  notification: Notification,
}

Here are a few action types that could be useful while developing your agent:

issueMention
issueEmojiReaction
issueCommentMention
issueCommentReaction
issueAssignedToYou
issueUnassignedFromYou
issueNewComment
issueStatusChanged

Permission Change Webhooks

Permission Change events are triggered when your agent gains or loses access to a team.

Enable this category by selecting Permission changes in your OAuth app config. The webhook will be of type PermissionChange with action teamAccessChanged.

The received webhook payload will have the following shape when team access is granted or removed:

{
  type: "PermissionChange",
  action: "teamAccessChanged",
  createdAt: string,
  organizationId: string,
  oauthClientId: string,
  appUserId: string,
  canAccessAllPublicTeams: boolean,
  addedTeamIds: string[],
  removedTeamIds: string[],
  webhookTimestamp: number,
  webhookId: string
}
 

You’ll receive a separate webhook when revoking your OAuth app:

{
  type: "OAuthApp",
  action: "revoked",
  createdAt: string,
  organizationId: string,
  oauthClientId: string,
  webhookTimestamp: number,
  webhookId: string
}
 

Existing integrations

When to build an integration or agent

If your integration primarily reads data from Linear or performs actions that should be attributed to individual team members, an integration is the right choice.

Build an agent if you want your application to appear as a distinct workspace member with its own identity and actions within Linear.

Convert an existing integration

If you have an existing Linear integration it can be converted to use the new authentication and gain the new functionality.

The new actor=app actor type works quite differently at the core to our legacy actor=application approach. However, if you are using actor=application today to request a token that is only used to create issues or comments as an app, then it is backwards compatible – you can simply change this parameter.

actor=application allows for dual-purpose authentication tokens that can be used both as the authenticating user in some circumstances and as an "app" in others. If you currently are using a token like this, then to migrate you will need to ask users to authenticate twice: once for their personal access and secondarily for the app installation.

Feedback, requests, questions

Please join the #api-agents channel in our community Slack to provide feedback on this guide, request API's, and interact with other engineers developing agentic integrations.