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¶
- Create an OAuth application at
https://linear.app/settings/api/applications/new(workspace admin). - Enable the agent capability and request scopes:
app:assignable-- lets the app be assigned as a delegate on issues and added to projects.app:mentionable-- lets the app be@-mentioned in issues, documents and comments.- 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 -- seekapelle_gateway.http_a2a's "bring your own ASGI app" convention, which this adapter follows too). - Copy the webhook signing secret -- store it wherever the deployment
keeps surface secrets (OpenBao
kapelle/surfaces/linear, matchingdocs/github-app.md's pattern once task 16 exposes that path to gateway; until then, plain config -- seekapelle_gateway.linear.tokens.EnvLinearTokenProvider). - Install the app on the workspace via the OAuth flow with
actor=appin 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. - 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 waydocs/github-app.md'sRealGitHubTokenProviderhas 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 teamacmefor 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 mountsLinearAdapter.handle_webhook(andGitHubAdapter'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.