Linear Triggers¶
A linear_issues workflow polls the Linear API at a configurable interval and dispatches a task to your agent for every matching issue it has not seen before. Unlike webhook triggers, no public endpoint is required - agentd reaches out to Linear rather than waiting for Linear to push events.
agentd Linear
────────────────────────────────── ──────────────────────
WorkflowRunner timer fires ──▶ GraphQL API
LinearStrategy fetches matching issues◀── Issue list response
Deduplicate by identifier (ENG-123)
Dispatch new issues as Tasks ──▶ Agent processes each issue
Prerequisites¶
1. Create a Linear personal API key¶
- Open Linear and go to Settings → API → Personal API keys
- Click Create key
- Give it a descriptive label (e.g.
agentd-worker) - Copy the generated key - it is shown only once
Key scope
Personal API keys have the same permissions as your Linear account. Use a dedicated service account or a limited-scope key for production deployments.
2. Set the environment variable¶
agentd reads the Linear API key from the AGENTD_LINEAR_API_KEY environment variable. Set it before starting the orchestrator:
For persistent configuration, add it to your shell profile or use a secrets manager:
# .env file (do not commit to version control)
AGENTD_LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# systemd service override
systemctl edit agentd-orchestrator
# Add:
# [Service]
# Environment=AGENTD_LINEAR_API_KEY=lin_api_...
If AGENTD_LINEAR_API_KEY is not set when a linear_issues workflow is created or starts polling, the orchestrator logs an error and the workflow runner exits.
Configuration¶
JSON (REST API)¶
{
"name": "linear-triage",
"agent_id": "<AGENT_UUID>",
"trigger_config": {
"type": "linear_issues",
"team_key": "ENG",
"status": ["Triage"],
"labels": ["agent"]
},
"prompt_template": "Triage Linear issue {{identifier}}: {{title}}\n\n{{body}}\n\nTeam: {{team}} | Priority: {{priority}}",
"poll_interval_secs": 120,
"enabled": true
}
Field reference:
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Must be "linear_issues" |
team_key |
string | No* | Linear team key (e.g. ENG). Filter to issues in this team only |
project |
string | No* | Linear project name filter. Issues must belong to this project |
status |
array of strings | No* | Issue state names to match (e.g. ["Triage", "Todo"]) |
labels |
array of strings | No* | Label names - issue must carry all listed labels |
assignee |
string | No* | Assignee display name or email address |
* At least one filter field must be provided. A config with no filters is rejected to prevent accidentally polling all issues in your workspace.
CLI¶
agent orchestrator create-workflow \
--name linear-triage \
--agent-name triage-agent \
--trigger-type linear-issues \
--linear-team-key ENG \
--linear-status "Triage" \
--linear-label "agent" \
--poll-interval 120 \
--prompt-template "Triage Linear issue {{identifier}}: {{title}}\n\n{{body}}"
YAML template (.agentd/)¶
name: linear-triage
agent: triage-agent
source:
type: linear_issues
team_key: ENG # Filter by team key
status: [Triage] # Only issues in "Triage" state
labels: [agent] # Must carry the "agent" label
# project: Backend # Optional: filter by project name
# assignee: alice@example.com # Optional: filter by assignee
poll_interval_secs: 120
enabled: true
prompt_template: |
Triage Linear issue {{identifier}}: {{title}}
{{body}}
Team: {{team_name}} ({{team}}) | Priority: {{priority}} | State: {{state}}
URL: {{url}}
Filter Options¶
Filters are applied server-side via the Linear GraphQL API. Only issues matching all provided filters are returned.
team_key¶
The short team identifier shown in issue identifiers (e.g. the ENG in ENG-123). Find team keys in Linear under Settings → Teams.
project¶
The display name of a Linear project. Issues without a project are excluded when this filter is set.
status¶
One or more workflow state names. Matches issues in any of the listed states. State names are case-sensitive and must match the names configured in your Linear team's workflow.
Common Linear default states: Triage, Backlog, Todo, In Progress, In Review, Done, Cancelled.
labels¶
One or more label names. An issue must carry all listed labels to match (AND logic). Label names are case-sensitive.
assignee¶
Filter by assignee display name or email address. Leave unset to match issues regardless of assignee (including unassigned).
Template Variables¶
Linear-specific variables available in prompt_template:
| Variable | Description | Example |
|---|---|---|
{{title}} |
Issue title | "Fix login timeout" |
{{body}} |
Issue description (markdown) | Full markdown content |
{{identifier}} |
Linear issue identifier | ENG-123 |
{{state}} |
Issue state name | Todo, In Progress |
{{priority}} |
Priority level | 0 (none), 1 (urgent), 2 (high), 3 (medium), 4 (low) |
{{team}} |
Linear team key | ENG |
{{team_name}} |
Linear team display name | Engineering |
{{project}} |
Linear project name (empty if unset) | Backend |
{{assignee}} |
Assignee display name (empty if unassigned) | Alice Example |
{{labels}} |
Comma-separated label names | "bug, urgent" |
{{url}} |
Linear issue URL | https://linear.app/myorg/issue/ENG-123 |
{{linear_id}} |
Internal Linear UUID | abc-uuid-... |
{{source_id}} |
Linear issue identifier (e.g. ENG-123) |
ENG-123 |
Priority values:
| Value | Meaning |
|---|---|
0 |
No priority |
1 |
Urgent |
2 |
High |
3 |
Medium |
4 |
Low |
Example template using Linear variables:
Triage Linear issue {{identifier}}: {{title}}
{{body}}
Team: {{team_name}} ({{team}})
Project: {{project}}
State: {{state}}
Priority: {{priority}}
Assignee: {{assignee}}
Labels: {{labels}}
URL: {{url}}
Please:
1. Review the issue description
2. Assess severity based on priority and labels
3. Suggest next steps or implementation approach
Deduplication¶
Each Linear issue has a human-readable identifier (e.g. ENG-123). agentd records processed identifiers so each issue triggers the agent at most once, even across restarts. An issue is re-dispatched only if:
- It was never processed (new issue matching the filters)
- The deduplication store is cleared (e.g. database reset)
Linear deduplication uses issue.identifier (the same value as {{source_id}} in templates). The internal UUID ({{linear_id}}) is available in templates for reference but is not used as the deduplication key.
Polling Behaviour¶
The poll_interval_secs field (in seconds, default 60) controls how often agentd queries the Linear API. At each poll:
- Linear GraphQL API is queried with the configured filters
- Each returned issue is checked against the dedup store
- New issues are converted to
Taskobjects and dispatched to the agent - Issue identifiers (e.g.
ENG-123) are recorded to prevent re-dispatch
Choosing a poll interval:
| Scenario | Recommended interval |
|---|---|
| Low-volume team (< 10 issues/day) | 300 (5 min) |
| Active team with real-time needs | 60 (1 min, default) |
| High-frequency triage | 30 (30 s) |
Linear API rate limits
Linear's API allows up to 1 500 requests per hour per API key. A poll_interval_secs of 60 with a single workflow uses ~60 requests/hour, well within limits. If you run many Linear workflows with the same key, increase the interval accordingly.
Webhook Setup (Optional)¶
For sub-second latency, configure a Linear webhook to push events to agentd instead of polling. This requires a public HTTPS endpoint.
Step-by-step¶
1. Create a webhook workflow in agentd:
agent orchestrator create-workflow \
--name linear-webhook-handler \
--agent-name worker \
--trigger-type webhook \
--webhook-secret "$(openssl rand -hex 32)" \
--prompt-template "Linear issue {{action}}: {{title}}\n\n{{body}}"
2. Expose the endpoint (development):
3. Register the webhook in Linear:
- Open Linear → Settings → API → Webhooks
- Click New webhook
- URL:
https://your-domain.com/webhooks/<WORKFLOW_ID> - Label:
agentd-worker(or any descriptive name) - Resource types: Select
Issues(and optionallyComments,Projects) - Team filter: Optionally restrict to a specific team
- Signing secret: Copy the secret you used with
--webhook-secretand paste it here - Click Create webhook
4. Verify:
In Linear's webhook settings, click Send test and confirm agentd responds with 202 Accepted.
Polling vs webhooks for Linear
Use linear_issues polling when you don't need sub-second latency or can't expose a public endpoint. Use the webhook trigger type when you need immediate reaction and have a public HTTPS endpoint available.
Troubleshooting¶
Workflow not picking up new Linear issues¶
- Check the API key - verify
AGENTD_LINEAR_API_KEYis set and valid: - Check filter values - team keys and state names are case-sensitive. Run:
and verify
trigger_configmatches your Linear team's actual keys and state names. - Check dispatch history -
agent orchestrator workflow-history <WORKFLOW_ID>shows recent dispatches. - Check logs - the orchestrator logs each Linear poll at
DEBUGlevel and each dispatch atINFO.
Issues dispatched multiple times¶
- This should not happen under normal operation. If it does, check for duplicate workflows polling the same filters.
- Do not clear the deduplication store while workflows are running.
AGENTD_LINEAR_API_KEY not found error¶
The environment variable must be set in the same environment where the orchestrator process runs. If using a process manager (systemd, launchd), set it in the service's environment configuration - not just your shell.
State name mismatch¶
Linear state names are workspace-configurable. "In Progress" in one workspace may be "In-Progress" in another. Find exact names in Linear under Settings → Teams → your team → Workflow.