Skip to content

Linear agent app: registration

Backlog task 32, design doc ยง13 "Linear". One Linear OAuth app per Kapelle deployment, installed as an app actor (not a per-user OAuth grant) on each workspace that wants Kapelle teams.

Verified against linear.app/developers (agents, agent-interaction, agent-best-practices, webhooks pages) and cross-checked against independent secondary sources at the time this was written -- Linear's GraphQL schema isn't publicly introspectable without an API key, so a few exact field names below (marked) are the best-available reading rather than a live schema dump. Fix forward if the live integration test (tests/linear/test_linear_live_integration.py) finds a mismatch.

Registering the app

  1. Create an OAuth application at https://linear.app/settings/api/applications/new (workspace admin).
  2. Enable the agent capability and request scopes:
  3. app:assignable -- lets the app be assigned as a delegate on issues and added to projects.
  4. app:mentionable -- lets the app be @-mentioned in issues, documents and comments.
  5. Enable the Agent session events webhook category, pointed at the gateway's Linear surface adapter endpoint (this task wires the receiving side, LinearAdapter.handle_webhook; the gateway's HTTP server mounting a route for it is a separate, not-yet-built piece -- see kapelle_gateway.http_a2a's "bring your own ASGI app" convention, which this adapter follows too).
  6. Copy the webhook signing secret -- store it wherever the deployment keeps surface secrets (OpenBao kapelle/surfaces/linear, matching docs/github-app.md's pattern once task 16 exposes that path to gateway; until then, plain config -- see kapelle_gateway.linear.tokens.EnvLinearTokenProvider).
  7. Install the app on the workspace via the OAuth flow with actor=app in the authorization URL -- this is what makes the installation an app actor (posts and mentions as "Kapelle", not as the installing human) instead of a per-user grant. A workspace admin must complete this step.
  8. Copy the resulting OAuth access token -- this is what LinearTokenProvider.get_access_token() returns; Linear app-actor tokens don't expire on the same short cycle GitHub installation tokens do, so there's no minting/refresh policy needed here the way docs/github-app.md's RealGitHubTokenProvider has for GitHub.

Env vars (matching the live integration test)

  • KAPELLE_LINEAR_WEBHOOK_SECRET: step 4's signing secret.
  • KAPELLE_LINEAR_ACCESS_TOKEN: step 6's access token.
  • KAPELLE_LINEAR_TEAM_KEY: the Linear team key (e.g. ENG) of a throwaway team/project to run the live test against, mapped to Kapelle team acme for the test.
  • KAPELLE_LINEAR_TEST_USER_ID: the Linear user id to act as the linked "team member" driving the test (find it via the GraphQL { viewer { id } } query with your own personal API key, or the Linear app settings page).

Delivering webhooks to localhost

Linear has no local-forwarding CLI of its own; use a generic tunnel (e.g. ngrok http <port>) pointed at wherever the gateway's HTTP server ends up mounting the Linear webhook route, and set that tunnel URL as the app's webhook URL for manual end-to-end testing. The live integration test itself doesn't need this -- like tests/github/test_github_live_ integration.py, it synthesizes real-shaped webhook payloads directly against LinearAdapter.handle_webhook rather than requiring Linear to actually deliver one over the network (see that test's module docstring for the full reasoning, which applies here unchanged).

What this task does NOT cover

  • The gateway HTTP route that would receive a real Linear-delivered webhook -- no gateway HTTP server/router exists yet outside kapelle_gateway.http_a2a's per-role A2A endpoint. Whoever builds that wiring mounts LinearAdapter.handle_webhook (and GitHubAdapter's, and Slack's) behind it.
  • OAuth token refresh/rotation flows -- app-actor access tokens are configured once per this doc's step 6; nothing here re-derives one.