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-here
Both 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 ASC
Other 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.
Verify the connection¶
Validate syntax¶
Check your configuration without making API calls:
sortie validate ./WORKFLOW.md
This 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.md
Watch the logs. A successful poll produces:
level=INFO msg="tick completed" candidates=3 dispatched=0 running=0 retrying=0
candidates=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 -5
If 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
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.