home / skills / 01000001-01001110 / agent-jira-skills / jira-transitions
This skill helps you move Jira issues through transitions, fetch available states, and set resolutions across tasks to streamline workflow.
npx playbooks add skill 01000001-01001110/agent-jira-skills --skill jira-transitionsReview the files below or copy the command above to add this skill to your agents.
---
name: jira-transitions
description: Move Jira issues through workflow states. Use when transitioning issues (To Do, In Progress, Done) or setting resolutions.
---
# Jira Transitions Skill
## Purpose
Move issues through workflow states. Get available transitions and execute status changes.
## When to Use
- Moving issues to different statuses (To Do → In Progress → Done)
- Getting available transitions for an issue
- Bulk transitioning issues
- Setting resolution when closing issues
## Prerequisites
- Authenticated JiraClient (see jira-auth skill)
- Issue transition permissions
- Knowledge of workflow structure
## Important Notes
**Transition IDs are NOT standardized** - they vary by:
- Jira instance
- Project
- Workflow configuration
**Always query available transitions first** before attempting to transition.
## Implementation Pattern
### Step 1: Define Types
```typescript
interface Transition {
id: string;
name: string;
to: {
id: string;
name: string;
statusCategory: {
id: number;
key: string;
name: string;
};
};
fields?: Record<string, {
required: boolean;
name: string;
allowedValues?: Array<{ id: string; name: string }>;
}>;
}
interface TransitionsResponse {
transitions: Transition[];
}
```
### Step 2: Get Available Transitions
```typescript
async function getTransitions(
client: JiraClient,
issueKeyOrId: string
): Promise<Transition[]> {
const response = await client.request<TransitionsResponse>(
`/issue/${issueKeyOrId}/transitions?expand=transitions.fields`
);
return response.transitions;
}
```
### Step 3: Find Transition by Name
```typescript
async function findTransitionByName(
client: JiraClient,
issueKeyOrId: string,
targetStatusName: string
): Promise<Transition | null> {
const transitions = await getTransitions(client, issueKeyOrId);
return transitions.find(
t => t.name.toLowerCase() === targetStatusName.toLowerCase() ||
t.to.name.toLowerCase() === targetStatusName.toLowerCase()
) || null;
}
```
### Step 4: Execute Transition
```typescript
interface TransitionOptions {
resolution?: { name: string } | { id: string };
comment?: string;
fields?: Record<string, any>;
}
async function transitionIssue(
client: JiraClient,
issueKeyOrId: string,
transitionId: string,
options: TransitionOptions = {}
): Promise<void> {
const body: any = {
transition: { id: transitionId },
};
if (options.resolution || options.fields) {
body.fields = { ...options.fields };
if (options.resolution) {
body.fields.resolution = options.resolution;
}
}
if (options.comment) {
body.update = {
comment: [
{
add: {
body: {
type: 'doc',
version: 1,
content: [
{
type: 'paragraph',
content: [{ type: 'text', text: options.comment }],
},
],
},
},
},
],
};
}
await client.request(`/issue/${issueKeyOrId}/transitions`, {
method: 'POST',
body: JSON.stringify(body),
});
}
```
### Step 5: High-Level Transition Helper
```typescript
async function moveIssueTo(
client: JiraClient,
issueKeyOrId: string,
targetStatus: string,
options: TransitionOptions = {}
): Promise<boolean> {
const transition = await findTransitionByName(client, issueKeyOrId, targetStatus);
if (!transition) {
console.error(`No transition found to status: ${targetStatus}`);
return false;
}
// Check if resolution is required
if (transition.fields?.resolution?.required && !options.resolution) {
// Default to "Done" resolution
options.resolution = { name: 'Done' };
}
await transitionIssue(client, issueKeyOrId, transition.id, options);
return true;
}
```
### Step 6: Bulk Transition
```typescript
async function bulkTransition(
client: JiraClient,
issueKeys: string[],
targetStatus: string,
options: TransitionOptions = {}
): Promise<{ success: string[]; failed: string[] }> {
const results = { success: [] as string[], failed: [] as string[] };
for (const issueKey of issueKeys) {
try {
const success = await moveIssueTo(client, issueKey, targetStatus, options);
if (success) {
results.success.push(issueKey);
} else {
results.failed.push(issueKey);
}
} catch (error) {
results.failed.push(issueKey);
}
}
return results;
}
```
## Common Transitions
Most Jira projects have these standard transitions:
| From Status | Transition Name | To Status |
|-------------|-----------------|-----------|
| To Do | Start Progress | In Progress |
| In Progress | Done | Done |
| In Progress | Stop Progress | To Do |
| Done | Reopen | To Do |
**Note**: These names vary by workflow configuration.
## Resolution Values
When transitioning to "Done", you often need a resolution:
| Resolution | Description |
|------------|-------------|
| Done | Work completed |
| Won't Do | Not planning to do |
| Duplicate | Already exists |
| Cannot Reproduce | Cannot reproduce issue |
## curl Examples
### Get Available Transitions
```bash
curl -X GET "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123/transitions?expand=transitions.fields" \
-H "Authorization: Basic $(echo -n 'email:token' | base64)" \
-H "Accept: application/json"
```
### Execute Transition (Simple)
```bash
curl -X POST "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123/transitions" \
-H "Authorization: Basic $(echo -n 'email:token' | base64)" \
-H "Content-Type: application/json" \
-d '{
"transition": { "id": "21" }
}'
```
### Transition with Resolution (for Done)
```bash
curl -X POST "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123/transitions" \
-H "Authorization: Basic $(echo -n 'email:token' | base64)" \
-H "Content-Type: application/json" \
-d '{
"transition": { "id": "31" },
"fields": {
"resolution": { "name": "Done" }
}
}'
```
### Transition with Comment
```bash
curl -X POST "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123/transitions" \
-H "Authorization: Basic $(echo -n 'email:token' | base64)" \
-H "Content-Type: application/json" \
-d '{
"transition": { "id": "21" },
"update": {
"comment": [
{
"add": {
"body": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "Moving to In Progress" }]
}
]
}
}
}
]
}
}'
```
## API Response (204 No Content)
A successful transition returns **204 No Content** with an empty body.
## Error Handling
### Common Errors
| Error | Cause | Solution |
|-------|-------|----------|
| 400 Bad Request | Invalid transition ID | Query transitions first |
| 400 Bad Request | Missing required resolution | Add resolution field |
| 403 Forbidden | No permission to transition | Check workflow permissions |
| 404 Not Found | Issue doesn't exist | Verify issue key |
### Error Response Example
```json
{
"errorMessages": [
"You must specify a resolution when transitioning issues to the 'Done' status."
],
"errors": {
"resolution": "Resolution is required."
}
}
```
## Workflow Discovery Pattern
```typescript
async function discoverWorkflow(
client: JiraClient,
issueKeyOrId: string
): Promise<Map<string, Transition[]>> {
// Get transitions from current state
const transitions = await getTransitions(client, issueKeyOrId);
console.log(`Available transitions from current state:`);
for (const t of transitions) {
console.log(` ${t.id}: ${t.name} → ${t.to.name}`);
if (t.fields?.resolution?.required) {
console.log(` (requires resolution)`);
}
}
return new Map([
['current', transitions]
]);
}
```
## Common Mistakes
- Using transition ID without querying first
- Forgetting resolution when moving to Done
- Assuming transition IDs are same across projects
- Not handling 204 response (empty body is success)
## References
- [Transitions API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-get)
## Version History
- 2025-12-10: Created
This skill moves Jira issues through workflow states and sets resolutions when required. It exposes helpers to list available transitions, find a transition by name, execute a transition, and run bulk transitions. Use it to reliably move issues (e.g., To Do → In Progress → Done) while handling required fields and comments.
The skill queries the Jira Cloud Transitions API to retrieve available transitions for a specific issue, including required fields. It locates a transition by name or target status, prepares the request body (including resolution, fields, and comments), and posts the transition to Jira. It returns success/failure results and supports bulk processing by iterating per-issue and collecting outcomes.
Why did my transition request return 400?
Typically the transition ID is invalid for that issue or a required field (often resolution) is missing; query available transitions and inspect transition.fields for required entries.
How do I know which resolution value to send?
Use resolution names used by your Jira instance (e.g., Done, Won't Do). Query your project or use an existing issue to read allowed resolution values if uncertain.
What indicates a successful transition?
A 204 No Content response indicates success. Do not expect a JSON body on success; treat 204 as confirmation.