How to Connect Sortie to Jira Cloud
This guide configures Sortie to poll issues from a Jira Cloud project, dispatch agents, and transition issues through your Jira workflow. By the end, you’ll have a working WORKFLOW.md that authenticates against your Jira instance, fetches the right issues, and reports back status changes.
Prerequisites
- Sortie installed and on your
PATH(installation guide) - Quick start completed with the file adapter (quick start)
- A Jira Cloud instance (Server and Data Center are not supported — the adapter uses REST API v3, which is Cloud-only)
- An API token from Atlassian account settings → Security → API tokens
- 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, 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 doesn’t 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 doesn’t 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 doesn’t match any project in your Jira instance. Verify the key in Jira’s project settings — it’s 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?