How to connect Sortie to Jira
This guide configures Sortie to poll issues from a Jira project, dispatch agents, and transition issues through your Jira workflow. By the end, you will have a working WORKFLOW.md that authenticates against your Jira instance, fetches the right issues, and reports back status changes.
Jira Server and Data Center: This guide walks through the Cloud setup (REST API v3, Basic auth with
email:token). If you are connecting to a self-hosted Jira Server or Data Center instance, see the Jira adapter reference for v2 configuration, Bearer/PAT authentication, and theapi_version: "2"field.
Prerequisites
- Sortie installed and on your
PATH(installation guide) - Quick start completed with the file adapter (quick start)
- A Jira Cloud instance with an API token, or a Jira Server / Data Center instance (see note above)
- An API token from Atlassian account settings: Security: API tokens (Cloud) or a Personal Access Token from your instance profile (Server / Data Center)
- Your Jira project key (the prefix on issue identifiers,
PROJinPROJ-42) - The workflow status names used in your project (e.g., “To Do”, “In Progress”, “Done”)
Create the API token
Generate a token in your Atlassian account settings. Sortie authenticates with Basic Auth for Cloud, which requires your email address and the token joined by a colon:
you@company.com:your-api-token-hereBoth parts must be non-empty. Sortie validates this format at startup and rejects values without a colon or with an empty side.
Store the credentials in environment variables:
export SORTIE_JIRA_ENDPOINT="https://yourcompany.atlassian.net"
export SORTIE_JIRA_API_KEY="you@company.com:your-api-token-here"The endpoint is the base URL of your Jira instance, no /rest/api/... suffix. Sortie rejects endpoints that include an API path.
Write the minimum configuration
Replace the tracker section in your WORKFLOW.md front matter:
---
tracker:
kind: jira
endpoint: $SORTIE_JIRA_ENDPOINT
api_key: $SORTIE_JIRA_API_KEY
project: PROJ
active_states: [To Do, In Progress]
terminal_states: [Done]
agent:
kind: claude-code
---
Fix {{ .issue.identifier }}: {{ .issue.title }}Three fields are required:
endpoint: Jira Cloud base URL. Sortie strips trailing slashes.api_key:email:tokenformat. Sent as a Base64-encoded Basic Auth header on every request.project: Jira project key. Must not be empty.
The $VAR syntax expands environment variables at config load time. endpoint and project expand only when the entire value is a variable reference ($VAR or ${VAR}). api_key expands variables anywhere in the string, so $SORTIE_JIRA_API_KEY works both ways.
If you omit active_states, Sortie defaults to ["Backlog", "Selected for Development", "In Progress"]. Override this to match your project’s actual workflow status names. State names are compared case-insensitively; "to do" matches Jira’s "To Do".
Scope issues with a query filter
By default, Sortie fetches all issues in active_states for the project. The query_filter field appends a raw JQL fragment to narrow the result:
tracker:
kind: jira
endpoint: $SORTIE_JIRA_ENDPOINT
api_key: $SORTIE_JIRA_API_KEY
project: PROJ
query_filter: "labels = 'agent-ready'"
active_states: [To Do, In Progress]
terminal_states: [Done]Sortie wraps your fragment in AND (...) and appends it to the base query. The resulting JQL for this example:
project = "PROJ" AND status IN ("To Do", "In Progress") AND (labels = 'agent-ready') ORDER BY priority ASC, created ASCOther useful filters:
# Only backend issues
query_filter: "component = Backend"
# Only issues assigned to me
query_filter: "assignee = currentUser()"
# Combination
query_filter: "component = Backend AND assignee = currentUser()"The filter applies to candidate fetches and state-change polls. It does not apply to reconciliation lookups (ID-based fetches of issues already dispatched) because those issues already passed filtering at dispatch time.
Configure handoff state
When an agent completes its work, Sortie can transition the issue to a specific state (a review column, a QA queue, or any reachable status in your Jira workflow):
tracker:
kind: jira
endpoint: $SORTIE_JIRA_ENDPOINT
api_key: $SORTIE_JIRA_API_KEY
project: PROJ
active_states: [To Do, In Progress]
handoff_state: Human Review
terminal_states: [Done]Sortie uses the Jira transitions API: it fetches available transitions for the issue, finds one whose target status matches handoff_state (case-insensitive), and executes it. If no matching transition exists (because the Jira workflow does not allow it from the current status), Sortie logs an error:
level=ERROR msg="transition failed" error="tracker: tracker_payload: no transition to state \"Human Review\" available for issue PROJ-42"Two constraints:
handoff_statemust not collide with any value interminal_states. Sortie rejects this at startup.- The transition must be available from the issue’s current Jira status. Check your Jira workflow diagram if transitions fail.
Configure dispatch-time transitions
Sortie can also transition an issue when the agent picks it up, moving it to an “In Progress” column so your team sees work has started:
tracker:
kind: jira
endpoint: $SORTIE_JIRA_ENDPOINT
api_key: $SORTIE_JIRA_API_KEY
project: PROJ
active_states: [To Do, In Progress]
in_progress_state: In Progress
handoff_state: Human Review
terminal_states: [Done]in_progress_state must be a value in active_states. If the issue is already in that state at dispatch time, the transition is skipped (debug log only). If the transition fails for other reasons (for example, the Jira workflow does not allow it), Sortie logs a warning and continues. The agent session proceeds regardless.
Three constraints:
in_progress_statemust appear inactive_states. Otherwise reconciliation would cancel the worker after the state change.in_progress_statemust not collide withterminal_statesorhandoff_state.- The API token needs write permissions (same as
handoff_state).
Enable tracker comments
Sortie can post comments on Jira issues at session lifecycle points (dispatch, completion, and failure). This creates a visible audit trail in the ticket without leaving Jira:
tracker:
# ... existing fields ...
comments:
on_dispatch: true
on_completion: true
on_failure: trueEach flag is independent. Enable only the events you care about. All default to false.
Comment failures are non-fatal. Sortie logs a warning and continues. The API token needs the same write permissions as handoff_state (write:jira-work or write:issue:jira).
See the workflow config reference for comment content details.
Verify the connection
Validate syntax
Check your configuration without making API calls:
sortie validate ./WORKFLOW.mdThis parses front matter, compiles the prompt template, and runs preflight checks. It catches missing fields, bad email:token format, and env vars that resolve to empty strings.
Test connectivity
Run a single poll cycle without dispatching agents:
sortie --dry-run ./WORKFLOW.mdWatch the logs. A successful poll produces:
level=INFO msg="tick completed" candidates=3 dispatched=0 running=0 retrying=0candidates=3 means Sortie found 3 issues in your active states (and matching your query_filter, if set). dispatched=0 is expected in dry-run mode; no agents are launched.
If candidates=0 and you expected results, check that your active_states values match Jira’s status names exactly (comparison is case-insensitive, but the names must otherwise match) and that your query_filter JQL is valid.
Troubleshoot authentication and API errors
Wrong credentials or expired token
level=ERROR msg="poll failed" error="tracker: tracker_auth_error: GET /rest/api/3/search/jql: 401"Verify your token is valid by testing it directly:
curl -s -u "you@company.com:your-api-token" \
"https://yourcompany.atlassian.net/rest/api/3/myself" | head -5If this returns your user profile, the token works. If it returns 401, regenerate the token.
CAPTCHA lockout
level=ERROR msg="poll failed" error="tracker: tracker_auth_error: GET /rest/api/3/search/jql: 401 (CAPTCHA challenge triggered; log in via browser to resolve)"Jira locked the account after repeated failed attempts. Log in to Jira through a browser, complete the CAPTCHA, then restart Sortie.
Project not found
level=ERROR msg="poll failed" error="tracker: tracker_not_found: GET /rest/api/3/search/jql: not found"The project key does not match any project in your Jira instance. Verify the key in Jira’s project settings; it is the short prefix, not the project name.
Rate limiting
level=WARN msg="poll failed" error="tracker: tracker_api: GET /rest/api/3/search/jql: rate limited (retry after 30 seconds)"Jira enforces rate limits on API calls. Sortie does not throttle client-side; it logs the response and waits for the next poll interval. If you hit this repeatedly, increase polling.interval_ms or narrow your query_filter to reduce result set size. Sortie paginates with a page size of 50, so large projects generate multiple API calls per poll.
Unreachable handoff transition
level=ERROR msg="transition failed" error="tracker: tracker_payload: no transition to state \"Human Review\" available for issue PROJ-42"The target state isn’t reachable from the issue’s current status in your Jira workflow. Open the Jira workflow editor and confirm that a transition exists from the expected source status to your handoff_state.
Full production example
---
tracker:
kind: jira
endpoint: $SORTIE_JIRA_ENDPOINT
api_key: $SORTIE_JIRA_API_KEY
project: PLATFORM
query_filter: "labels = 'agent-ready'"
active_states:
- To Do
- In Progress
in_progress_state: In Progress
handoff_state: Human Review
terminal_states:
- Done
- Won't Do
polling:
interval_ms: 60000
workspace:
root: ~/workspace/sortie
agent:
kind: claude-code
max_turns: 3
---
You are a senior engineer. Your work is tracked by Sortie.
## Task
**{{ .issue.identifier }}**: {{ .issue.title }}
{{ if .issue.description }}
### Description
{{ .issue.description }}
{{ end }}
{{ if .issue.labels }}
**Labels:** {{ .issue.labels | join ", " }}
{{ end }}
{{ if .issue.url }}
**Ticket:** {{ .issue.url }}
{{ end }}This configuration polls every 60 seconds, picks up issues labeled agent-ready in “To Do” or “In Progress,” runs up to 3 agent turns per issue, and moves completed issues to “Human Review.” For the full set of configuration options, see the WORKFLOW.md reference. For prompt template syntax, see How to write a prompt template.
Was this page helpful?