Linear
Read Linear issues into a workspace by team, and write back as the Oxagen app.
The Linear collector is not available yet. This page describes how it works once Oxagen turns it on.
Account to connect with
An organization Owner, or a workspace Admin, signs in to Linear and approves the Oxagen app for the Linear workspace. The grant acts as the Oxagen app (actor=app), not as the person. Every write shows in Linear as the Oxagen app.
The sign-in needs the LINEAR_OAUTH_CLIENT_ID setting, and the webhook needs LINEAR_WEBHOOK_SECRET, the shared secret Linear locks each delivery with. Sign-in uses PKCE, so no client secret is needed.
Scopes asked for
| Scope | Asked | Used for |
|---|---|---|
read | Always | Reading issues, teams, and workflow states |
write | Only when the person turns on writing | Comments, state changes, priority, and labels |
Oxagen asks for no admin scope. It registers no webhook of its own, so it does not need one.
Scope choices
A collector names one or more teams by key, such as ENG. The scope step lists the teams the connection can read. An issue that moves to another team keeps its Oxagen item, and the collector stops reading it when the new team is not in scope.
# work/collectors/linear-eng.toml in the steering repo
schema = "collector/v1"
name = "linear-eng"
label = "Linear engineering"
type = "linear"
connection = "<connection id>"
[scope]
teams = ["ENG", "OPS"]
repos = ["acme/app"]
create_team = "ENG"repos is optional. It names the linked repositories whose work items this collector creates issues for. Without it the collector creates no issue. create_team names the team the issues go to. Leave it out only when the scope names one team. Until the collector settings page shows these fields, set them in the collector file or with set_work_collector.
A reconcile fails when the connection can no longer read a named team. After three failed reconciles in a row the collector reads failing and stops polling until an admin reconnects it.
Default field mapping
A workspace with no Linear rows gets these. They use Linear's workflow state types, which every team shares.
| Oxagen | Reads from Linear | Writes to Linear |
|---|---|---|
| Status Open | State types triage, backlog, unstarted, started | The team's first unstarted state |
| Status Closed | State types completed, canceled, duplicate | The team's first completed state |
| Resolution Done | State type completed | The first completed state |
| Resolution Canceled | State type canceled | None |
| Resolution Duplicate | State type duplicate | None |
| Resolution Won't do | None | The team's first canceled state |
| Priority P0, P1, P2, P3 | Priority 1 Urgent, 2 High, 3 Medium, 4 Low | The same numbers |
| Type Bug, New Feature, Improvement, Documentation, Test, Chore | Labels Bug, Feature, Improvement, Documentation, Test, Chore | The same labels |
Linear priority 0 means no priority and maps to none. A trashed issue reads as closed. You can change a status write row to name a state instead of a state type.
What each switch writes
| Switch | Linear call | Result |
|---|---|---|
certify_note | commentCreate | A comment that lists the criteria |
send_note | commentCreate | A comment about the send |
status | issueUpdate with stateId | Moves the issue to a state of its own team, chosen by the write rows |
close | issueUpdate with stateId | Moves the issue to the team's first completed state |
labels | issueUpdate | Sets the priority number and the Type label, and swaps the Type label, keeping every unrelated label |
create | fileUpload, issueCreate, attachmentCreate | Uploads each file, creates the issue in create_team with the mapped priority, labels, and state, and attaches each file |
text | issueUpdate | Sets the title and description a person edited in Oxagen |
badge | None | Writes nothing. A Linear issue gets no work item badge yet |
The created issue's id comes from the work item, so a retry finds the issue an earlier try made. A label no mapping row names is set only where the team or workspace has it.
A label the team and workspace do not have fails the write once as mapping_missing, and the message says to create the label in Linear or change the mapping. Oxagen creates no label and no state. A mapping that names a field Linear cannot set, or a priority outside 0 to 4, fails the same way.
What the tracker cannot do
- Linear has no resolution field. A state carries the resolution, so a won't-do needs a canceled state in the team.
- A write targets a state of the issue's own team. A team with no state of the mapped type fails as
status_unreachable. - Oxagen writes a title or a description only for an issue it creates, and for a person's edit in Oxagen. It never sets an assignee.
- One webhook serves every installation of the app, so Oxagen cannot register or renew a webhook per team.
Webhook and reconcile timing
Linear locks each delivery with the shared secret and sends the lock in Linear-Signature, the hex HMAC-SHA256 of the raw body, so any change to the body shows. Oxagen refuses a delivery whose webhookTimestamp, inside the locked body, is more than a minute from now. The webhook arrives within seconds of a change. It names only the issue id, and Oxagen reads the issue over GraphQL.
The webhook never expires, so Oxagen renews nothing. A reconcile every 15 minutes reads the issues changed since its cursor. Press Read now on the collector to start one sooner.