Linear
Inbound webhook → swarm tasks. Workflow-state gate, swarm-ready label override, AgentSession activity stream, and how to push outbound updates.
Agent Swarm integrates with Linear as an inbound issue-tracker source: assigning the agent to a Linear issue creates a swarm task and a Linear AgentSession. Activity flows back into the AgentSession as thought / action / response / error events.
Sync direction is INBOUND-ONLY (Linear → swarm). Inbound webhooks create and update swarm tasks. Outbound state changes (e.g. moving an issue to "Done", adding labels, posting plain comments outside an AgentSession) are not automatic — see Outbound updates below.
What it does
- Inbound on assignment. When a user assigns the Linear agent integration to an issue, Linear fires an
AgentSessionwebhook. The handler creates a swarm task linked to the session. - State-gated task creation. Only issues whose
WorkflowState.typeis in the configured allowlist trigger a task. By default that'sunstarted, started, completed, canceled— i.e. everything excepttriageandbacklog. Skipped assignments leave a comment on the AgentSession explaining how to retry. - Label override. A label on the issue (default
swarm-ready, configurable) bypasses the state gate so users can pre-stage backlog issues to auto-trigger when assigned. - AgentSession activity stream. The agent posts thoughts, actions, responses, and errors back to the AgentSession in real time. A
responseactivity auto-completes the session. - Webhook signing + dedup. Inbound webhooks are HMAC-SHA256 verified and deduped by the
Linear-Deliveryheader (5-minute TTL). - Dashboard linking.
externalUrlson each session link to the swarm dashboard's task page.
Setup
1. Create a Linear OAuth App
- Go to Linear → Settings → API → Applications and create a new application:
- Actor: Application
- Callback URL:
<MCP_BASE_URL>/api/trackers/linear/callback - Webhook URL:
<MCP_BASE_URL>/api/trackers/linear/webhook
- Enable Agent session events in the webhook settings.
- Copy the Client ID, Client Secret, and Webhook Signing Secret.
2. Configure environment
Add to your .env:
# Required — OAuth + webhook auth
LINEAR_CLIENT_ID=your-client-id
LINEAR_CLIENT_SECRET=your-client-secret
LINEAR_REDIRECT_URI=http://localhost:3013/api/trackers/linear/callback
LINEAR_SIGNING_SECRET=your-webhook-signing-secret
# Optional — gate config (defaults shown)
LINEAR_ALLOWED_STATES=unstarted,started,completed,canceled
LINEAR_SWARM_READY_LABEL=swarm-ready
# Optional — disable the integration entirely
# LINEAR_DISABLE=trueWith portless dev mode (bun run dev:http):
LINEAR_REDIRECT_URI=https://api.swarm.localhost:1355/api/trackers/linear/callback3. Complete OAuth
Start the server and visit <MCP_BASE_URL>/api/trackers/linear/authorize in a browser to complete the OAuth flow. The token is encrypted at rest in swarm_config.
How it works
Inbound flow
State gate
When an issue is assigned, the handler resolves the issue's WorkflowState.type and label list. The decision is:
| Condition | Result |
|---|---|
Issue has the swarm-ready label (or your LINEAR_SWARM_READY_LABEL value) | Create — label override |
state.type ∈ LINEAR_ALLOWED_STATES | Create — ready |
state.type is unset / null | Create — fail-open |
state.type ∉ allowlist | Skip — post a response on the AgentSession explaining how to retry |
LINEAR_ALLOWED_STATES is a comma-separated list of Linear WorkflowState.type values. The full enum is triage, backlog, unstarted, started, completed, canceled. Whitespace and case are normalized.
Examples:
# Default: skip Backlog and Triage
LINEAR_ALLOWED_STATES=unstarted,started,completed,canceled
# Only trigger when actively in progress
LINEAR_ALLOWED_STATES=started
# Open the gate completely (skip everything → only label override works)
LINEAR_ALLOWED_STATES=The Linear AgentSessionEvent payload doesn't include state or labels, so the handler issues a small GraphQL query (issue.state.type + labels.nodes.name) to resolve them. If the OAuth token is missing, the gate fails open.
Skip message
When the gate skips an assignment, the agent posts a response activity to the AgentSession (which auto-completes it):
Agent Swarm received the assignment but skipped — this issue is in Backlog.
To trigger work, move it to an allowed workflow state (e.g. Todo or In Progress), or add the
swarm-readylabel and re-assign the agent.
This is a deliberate design choice: silent no-ops on assignments are confusing. Always leave a trace.
Issue updates
Linear Issue updates that target a tracked issue refresh the swarm task's tracker metadata. State transitions:
| Linear state | Swarm action |
|---|---|
Backlog | log only, no status change |
Todo | log only |
In Progress | log only |
Done | log only — agent decides when work is done |
Canceled / Cancelled | cancel the swarm task |
| Issue deleted | cancel the swarm task |
Follow-up messages
When a user sends a message in the Linear agent chat after the original task is done:
- Linear fires a
promptedAgentSessionEvent. - The handler creates a new swarm task with the follow-up context, repointing the existing
tracker_syncto it. - The agent processes the follow-up via the normal task lifecycle.
If the original task is still in flight, the handler posts a thought activity acknowledging the message but does not create a new task — the message is folded into the existing run.
Stop signal
If the user clicks the stop button in Linear, the agent activity carries signal: "stop". The handler cancels the active swarm task and ends the session with a "Task cancelled by user." response.
Outbound updates
The webhook integration is one-way. To push updates back to Linear from the swarm — creating issues, transitioning states, posting comments outside an AgentSession — use the linear-interaction skill. The skill wraps the Linear GraphQL API:
mutation IssueCreate($input: IssueCreateInput!) {
issueCreate(input: $input) { issue { id identifier } }
}
mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) {
issueUpdate(id: $id, input: $input) { success }
}
mutation CommentCreate($input: CommentCreateInput!) {
commentCreate(input: $input) { comment { id } }
}Authentication uses the swarm's OAuth token from swarm_config (stored encrypted under LINEAR_OAUTH_TOKEN). The lead agent fetches it via db-query; worker agents request it from the lead.
For activity within an existing AgentSession (the agent's running task), use the helpers in src/linear/sync.ts — postAgentSessionResponse, postAgentSessionThought, postAgentSessionAction, postAgentSessionError. These don't need the skill; they're called directly from the worker session.
MCP tools
| Tool | Description |
|---|---|
tracker-status | Check tracker connection status |
tracker-link-task | Link a swarm task to a Linear issue |
tracker-unlink | Remove a tracker link |
tracker-sync-status | View sync status for linked items |
tracker-map-agent | Map a swarm agent to a Linear user for assignment routing |
Architecture
The integration sits on top of a generic tracker abstraction shared with Jira and (eventually) others:
src/oauth/— Reusable OAuth module withoauth_apps/oauth_tokenstables and PKCE support.src/be/db-queries/tracker.ts— Generictracker_syncandtracker_agent_mappingtables.src/linear/— Linear-specific webhook handler (webhook.ts), AgentSession sync (sync.ts), state gate (gate.ts), and prompt templates (templates.ts).
The state gate is exposed as a pure function (shouldCreateTaskFromLinearEvent in src/linear/gate.ts) so it can be unit-tested without spinning up the API.
Related skills & references
linear-interactionskill — canonical procedure for pushing outbound updates (create issues, change status, comment) via the Linear GraphQL API. Invoke with theSkilltool.- Linear API docs — GraphQL schema and authentication reference.
@linear/sdk— official TypeScript SDK; the swarm uses rawfetchagainst the GraphQL endpoint, but the SDK's type definitions are the canonical reference for payload shapes.
Related docs
- Environment Variables — full env reference
- Task Lifecycle — how tasks created from Linear flow through the swarm
- Architecture Overview — system architecture including integrations
Integrations
External tools the swarm can talk to — issue trackers, chat platforms, error monitoring, email, and more.
Kapso (WhatsApp)
Inbound WhatsApp messages become swarm tasks via Kapso; agents reply in-thread. Native webhook handler, KV-backed number routing, outbound send/reply MCP tools, and the kapso-whatsapp skill for everything beyond plain text.